Доброго new Date().getTimeOfDay();

HTML5 Canvas незаменим, когда нужно что-то динамически нарисовать. Но если мы захотим что-то динамически изменять — нам придётся хранить состояние элементов и перерисовывать при необходимости.
Если мы захотим реагировать на события — нам придётся ловить координаты мыши и определять, находятся ли они внутри нужной фигуры.
И т.д.
Частые повторяющиеся задачи. Так и появляются фреймворки и библиотеки.
Впрочем, случай с Graphics2D.js немного другой: мне просто захотелось порисовать. С объектной моделью, анимацией и событиями. И — ничего лишнего.
Но максимально расширяемо: идей много, и всё можно вынести в плагины.
keyten.github.io/Graphics2D
(русская версия сайта будет сегодня-завтра)
Итак…
Начинаем
Контекст:
var ctx = Graphics2D.id('mycanvas'); // ну или так: var ctx = Graphics2D.query('canvas', 1); // второй <canvas> // или var ctx = Graphics2D.query( document.getElementById('mycanvas') );
Нарисуем… ну, например, небольшой круг, который будет анимироваться при наведении мыши:
ctx.circle({ cx : 300, cy : 300, radius : 50, fill : '#f0a' }).mouseover(function(){ this.animate({ scale : 2, opacity : 0.5, }, 300); }).mouseout(function(){ this.animate({ scale : 0.5, opacity : 1 }, 300); });
jsfiddle.net/wzemyho6
А теперь… пусть их будет 100:
for(var i = 0; i < 100; i++){ ctx.circle({ cx : Math.floor(Math.random() * 700), cy : Math.floor(Math.random() * 400), radius : 10, fill : 'rgb(' + [Math.floor(Math.random() * 255), Math.floor(Math.random() * 255), Math.floor(Math.random() * 255)].join(',') + ')' }).mouseover(function(){ this.animate({ radius : 20, opacity : 0.5, }, 300); }).mouseout(function(){ this.animate({ radius : 10, opacity : 1 }, 300); }); }
jsfiddle.net/9v63govv/4
Что удивляет — неплохая производительность: анимация начинает ощутимо тормозить начинает при 2-3 тысячах.
UPD. С введением requestAnimationFrame (большое спасибо Gerh) ситуация очень сильно улучшилась
Объекты
Встроенных рисуемых объектов 6: rect, circle, path, image, text и textblock.
Все фигуры наследуются от внутреннего класса Shape, который содержит большинство методов, изменяющих объект (трансформации, анимация, события, заливка, обводка, прозрачность...).
Разница между text и textblock: второй умеет переносить строки (автоматически и вручную через \n), в качестве координат указываются координаты блока, а не надписи.
В объекте path — кривые — квадратичные и кубические Безье, эллиптические… всё, что позволяет canvas. И вдобавок, всё расширяемо: например, один из плагинов добавляет рисование Catmull-Rom.
Любой объект мы можем создавать, указывая параметры по порядку, либо в объекте (последний позволяет дополнительные параметры:
ctx.circle(150, 150, 70, 'red', '2px black'); // fill, stroke ctx.circle({ cx : 150, cy : 150, radius : 70, fill : 'red', stroke : '5px dot red 0.5 round', opacity : 0.5 // а вот дополнительный параметр });
В любом объекте мы можем указывать заливку и обводку одновременно, причём последняя позволяет сразу несколько параметров.
А вот градиент:
var rect = ctx.rect(100, 100, 200, 200, { colors : ['red', 'green', 'blue'], from : 'top', to : 'bottom' });
Также можно создать градиент отдельным объектом ctx.gradient и залить им сразу несколько фигур. А потом любое изменение градиента будет отражаться на всех фигурах.
Да и инлайновый градиент тоже экземпляр класса градиентов, например, изменим один из цветов:
rect.fill().color(0, 'yellow');
Пути рисуются одним из трёх вариантов:
ctx.path("M10,10 L200,200 Z", null, "2px blue"); ctx.path([ [10,10], [200,200], [400,100,450,150] ]); ctx.path([ { name : 'moveTo', arguments:[10,10] }, { name : 'lineTo', arguments:[200,200] }, { name : 'closePath' } ]);
Объекты без заливки и обводки не рисуются, но могут реагировать на события (это нужно включить функцией
path.events(true)… так будет через пару дней).Строковый формат — не SVG, хотя поддерживает полный его синтаксис (пропуск пробелов перед минусов, пропуск повторяющихся функций и т.п.). Поддерживает только функции M, L, C, Q и Z (только абсолютные координаты) — moveTo, lineTo, bezier, quadratic и closePath.
На днях будет плагин, добавляющий полную поддержку SVG-путей :)
Можно обрабатывать отдельные точки путей:
path.point(0).name; // -> moveTo path.point(0).set('x', 20); path.before(1, 'L20,20 L30,50');
Нативный контекст
Мы можем создать функцию, рисующую на нативном контексте (например, чтобы оптимизировать какую-то медленную функциональность) и добавить её в перерисовку:
ctx.push({ draw : function(ctx){ ctx.fillRect(200, 200, 10, 10); } });
При большом желании можно добавить также обработку событий (просто добавить функцию isPointIn)… Или даже унаследовать от Graphics2D.Shape (получив кучу функций для изменения стилей и трансформации)…
Впрочем, это отдельная тема, о которой я, при желании хабрачитателей, расскажу.
Кроме того, большинство фигур и их методов умеют принимать CSS-значения, например:
ctx.rect("10pt", "10pt", "2em", "2em", "blue")Это функциональность, в смысле которой я до сих пор не уверен (будет интересно увидеть ваши комментарии на эту тему).
Плагины
Как я уже упоминал, Graphics2D достаточно расширяем, вот некоторые уже существующие плагины:
— Sprite — просто-спрайты и спрайтовая анимация.
— ImageAnim — анимация, когда разные кадры в разных файлах.
— CatmullRom — рисование кривых Catmull-Rom (в рамках объекта Path).
Планируются и другие (расширенная обработка событий, поддержка SVG-путей...) — как я говорил, идей много, и всё пойдёт в плагины.
keyten.github.io/Graphics2D
(русская версия сайта будет сегодня-завтра)
Github: github.com/keyten/Graphics2D.
Лицензия: MIT / LGPL.
Некоторые демо: Bezier, Gradients, Transformations, Textblock.
На этом всё, интересно услышать ваши отзывы.
И ещё: большое спасибо TheShock-у за немалую помощь.
