В этой статье я познакомлю вас с Fabric.js — мощной библиотекой JavaScript, которая упрощает работу с элементом HTML5 canvas. Fabric предоставляет отсутствующую объектную модель для canvas, а также анализатор SVG, слой интерактивности и целый набор других необходимых инструментов. Это проект с полностью открытым исходным кодом, лицензированный в рамках MIT, со многими вкладами на протяжении многих лет.
Я начал разрабатывать с Fabric три года назад после того, как обнаружил трудности работы с нативным API Canvas. Я создавал интерактивный редактор дизайна для printio.ru — моего стартапа, который позволяет пользователям создавать свои собственные вещи. Вид интерактивности, который я хотел, существовал только в приложениях Flash в те дни. Сейчас очень мало библиотек приближаются к тому, что возможно с Fabric, поэтому давайте рассмотрим подробнее.
Почему ткань?
В наши дни Canvas позволяет создавать в сети совершенно потрясающую графику , но API, который он предоставляет, неутешительно низок . Одно дело, если вы просто хотите нарисовать несколько базовых фигур на холсте и забыть о них. Если вам нужно какое-либо взаимодействие, чтобы изменить картинку в любой точке или нарисовать более сложные фигуры, ситуация кардинально меняется. Ткань стремится решить эту проблему.
Нативные методы холста позволяют вам запускать только простые графические команды, слепо изменяя все растровое изображение холста. Хотите нарисовать прямоугольник? Используйте fillRect (слева, сверху, ширина, высота). Хотите нарисовать линию? Используйте комбинацию moveTo (слева, сверху) и lineTo (x, y). Это похоже на то, как будто вы рисуете холст кистью, намазывая все больше масла или акрила сверху, с очень небольшим контролем.
Вместо того, чтобы работать на таком низком уровне, Fabric предоставляет простую, но мощную объектную модель поверх собственных методов. Он заботится о состоянии холста и рендеринге и позволяет напрямую работать с объектами.
Вот простой пример, демонстрирующий эту разницу. Допустим, вы хотите нарисовать красный прямоугольник где-нибудь на холсте. Вот как вы могли бы сделать это с нативным API Canvas:
// ссылка на элемент canvas (с id = "c") var canvasEl = document.getElementById ('c'); // получаем 2d контекст для рисования («растровое изображение», упомянутое ранее) var ctx = canvasEl.getContext ('2d'); // установить цвет заливки контекста ctx.fillStyle = 'red'; // создаем прямоугольник в точке 100,100 с размерами 20x20 ctx.fillRect (100, 100, 20, 20);
Код ниже показывает, как вы делаете то же самое с Fabric. Результат обоих подходов показан на рисунке 1.
// создаем обертку вокруг нативного элемента canvas (с id = "c") var canvas = new fabric.Canvas ('c'); // создаем объект прямоугольника var rect = new fabric.Rect ({ осталось: 100, верх: 100, заполнить: «красный», ширина: 20, высота: 20 }); // "добавить" прямоугольник на холст canvas.add (прямоугольник);
Рис. 1. Красный прямоугольник, нарисованный методами ткани или нативного холста
На данный момент, почти нет разницы в размере прямоугольника — два примера очень похожи. Однако вы уже можете увидеть, насколько отличается подход к работе с canvas. С нативными методами вы работаете с контекстом — объектом, представляющим все растровое изображение холста. В Fabric вы работаете с объектами — вы создаете их экземпляры, изменяете их свойства и добавляете их на холст. Вы можете видеть, что эти объекты являются первоклассными гражданами на Земле Ткани.
Рендеринг простого красного прямоугольника слишком прост. Вы можете по крайней мере повеселиться с ним и, возможно, немного формы. Давайте попробуем 45 градусов, сначала используя нативные методы холста:
var canvasEl = document.getElementById ('c'); var ctx = canvasEl.getContext ('2d'); ctx.fillStyle = 'red'; ctx.translate (100, 100); ctx.rotate (Math.PI / 180 * 45); ctx.fillRect (-10, -10, 20, 20);
А вот как вы это делаете в Fabric. (См. Рисунок 2 для результатов).
var canvas = new fabric.Canvas ('c'); // создаем прямоугольник с углом = 45 var rect = new fabric.Rect ({ осталось: 100, верх: 100, заполнить: «красный», ширина: 20, высота: 20, угол: 45 }); canvas.add (прямоугольник);
Рис. 2. Красный, повернутый прямоугольник, нарисованный методами ткани или нативного холста
Что тут происходит? Все, что вам нужно сделать в Fabric, это изменить значение угла объекта на 45. Однако при использовании нативных методов требуется больше работы. Помните, что вы не можете оперировать объектами. Вместо этого вы должны настроить
позиционирование и угол растрового изображения всего холста (ctx.translate, ctx.rotate) в соответствии с вашими потребностями. Затем вы снова рисуете прямоугольник, не забывая правильно сместить растровое изображение (-10, -10), чтобы оно по-прежнему отображалось в точке 100,100. В качестве бонуса вы должны переводить градусы в радианы при повороте растрового изображения холста.
Я уверен, что вы начинаете понимать, почему существует Fabric и сколько скрывает шаблон низкого уровня.
Давайте рассмотрим другой пример: отслеживание состояния холста.
Что если в какой-то момент вы захотите переместить прямоугольник в немного другое место на холсте? Как вы можете сделать это, не имея возможности работать с объектами? Вы бы просто вызвали еще один fillRect на холст растрового изображения? Не совсем. Вызов другой команды fillRect фактически рисует прямоугольник поверх всего, что уже нарисовано на холсте. Чтобы переместить прямоугольник, необходимо сначала стереть любое ранее нарисованное содержимое, а затем нарисовать прямоугольник в новом месте (см. Рисунок 3).
var canvasEl = document.getElementById ('c'); ... ctx.strokRect (100, 100, 20, 20); ... // стираем всю область холста ctx.clearRect (0, 0, canvasEl.width, canvasEl.height); ctx.fillRect (20, 50, 20, 20);
Вот как это можно сделать с помощью Fabric:
var canvas = new fabric.Canvas ('c'); ... canvas.add (прямоугольник); ... rect.set ({слева: 20, сверху: 50}); canvas.renderAll ();
Рисунок 3 Красный прямоугольник, нарисованный в новом месте
Обратите внимание на очень важное различие: с Fabric вам не нужно стирать содержимое, прежде чем пытаться изменить какой-либо контент. Вы по-прежнему работаете с объектами, просто изменяя их свойства, а затем снова визуализируете холст, чтобы получить свежее изображение.
Объекты
В последнем разделе вы увидели, как работать с прямоугольниками, создавая конструктор fabric.Rect. Ткань, конечно, охватывает и другие основные формы — круги, треугольники, эллипсы и так далее. Формы выставляются под «пространством имен» ткани как ткань. Круг, ткань. Треугольник, ткань. Эллипс и так далее. Ткань обеспечивает семь основных форм:
Чтобы нарисовать круг, просто создайте объект круга и добавьте его на холст.
var circle = new fabric.Circle ({ радиус: 20, заливка: «зеленый», слева: 100, сверху: 100 }); var triangle = new fabric.Triangle ({ ширина: 20, высота: 30, заливка: «синий», слева: 50, сверху: 50 }); canvas.add (круг, треугольник);
Вы делаете то же самое с любой другой базовой формой. На рисунке 4 показан пример зеленого круга, нарисованного в точке 100, 100 и синего треугольника в точке 50, 50.
Рисунок 4. Синий треугольник и зеленый круг, нарисованный тканью.
Манипулирование объектами
Создание графических объектов — прямоугольников, окружностей или чего-то еще — это только начало. В какой-то момент вам, вероятно, потребуется изменить ваши объекты. Возможно, определенное действие вызовет изменение состояния или воспроизведет какую-то анимацию. Или вы можете изменить свойства объекта (такие как цвет, непрозрачность, размер, положение) при определенных взаимодействиях с мышью.
Ткань заботится о визуализации холста и управлении состоянием для вас. Нам нужно только изменить сами объекты. В предыдущем примере демонстрировался метод set и то, как вызов set ({left: 20, top: 50}) переместил объект из его предыдущего местоположения. Аналогичным образом вы можете изменить любое другое свойство объекта.
Как и следовало ожидать, объекты Fabric имеют свойства, связанные с позиционированием (слева, сверху), размерами (ширина, высота), рендерингом (заливка, непрозрачность, обводка, strokeWidth), масштабированием и вращением (scaleX, scaleY, angle) и переворачиванием ( flipX, flipY). Да, создать перевернутый объект в Fabric так же просто, как установить для свойства flip * значение true.
Вы можете прочитать любое из этих свойств с помощью метода get и установить их с помощью set. Вот пример того, как изменить некоторые свойства красного прямоугольника. Рисунок 5 показывает результаты.
var canvas = new fabric.Canvas ('c'); ... canvas.add (прямоугольник); rect.set ('fill', 'red'); rect.set ({strokeWidth: 5, stroke: 'rgba (100,200,200,0,5)'}); rect.set ('angle', 15) .set ('flipY', true);
Рисунок 5 Красный, повернутый, обведенный прямоугольником, нарисованный тканью
Во-первых, значение заполнения устанавливается на «красный». Следующий оператор устанавливает значения strokeWidth и stroke, давая прямоугольнику штрих размером 5 пикселей светло-зеленого цвета. Наконец, код изменяет свойства angle и flipY. Обратите внимание, что каждый из трех операторов использует немного другой синтаксис.
Это показывает, что множество является универсальным методом. Вы, вероятно, будете использовать его довольно часто, и это должно быть максимально удобно. Как насчет добытчиков? Существует универсальный метод get, а также ряд конкретных. Чтобы прочитать свойство width объекта, вы используете get (‘width’) или getWidth (). Чтобы получить значение scaleX, вы должны использовать get (‘scaleX’), getScaleX () и так далее. Есть метод getWidth или getScaleX для каждого из «общедоступных» свойств объекта (обводка, strokeWidth, angle и т. Д.).
Возможно, вы заметили, что в предыдущих примерах объекты создавались с таким же хешем конфигурации, что и тот, который мы только что использовали в методе set. Вы можете «настроить» объект во время создания или использовать метод set позже:
var rect = new fabric.Rect ({width: 10, height: 20, fill: '# f55', opacity: 0.7}); // или функционально идентичный var rect = new fabric.Rect (); rect.set ({ширина: 10, высота: 20, заливка: '# f55', непрозрачность: 0,7});
Параметры по умолчанию
В этот момент вы можете задаться вопросом, что происходит, когда вы создаете объект, не передавая объект «конфигурации». У этого все еще есть те свойства?
Да. Когда определенные параметры не указываются при создании, объекты в Fabric всегда имеют набор свойств по умолчанию. Вы можете использовать следующий код, чтобы убедиться в этом:
var rect = new fabric.Rect (); // заметим, что в rect.getWidth (); // 0 rect.getHeight (); // 0 rect.getLeft (); // 0 rect.getTop (); // 0 rect.getFill (); // ргб (0,0,0) rect.getStroke (); // значение NULL rect.getOpacity (); // 1
Этот прямоугольник имеет набор свойств по умолчанию. Он расположен на 0,0, черный и полностью непрозрачный, не имеет обводки и размеров (ширина и высота 0). Поскольку размеры не указаны, вы не можете увидеть их на холсте. Если вы укажете положительные значения ширины и высоты, в левом верхнем углу холста появится черный прямоугольник, как показано на рисунке 6.
Рисунок 6. Как выглядит прямоугольник по умолчанию при заданных размерах
Иерархия и Наследование
Тканевые объекты не существуют независимо друг от друга. Они образуют очень четкую иерархию. Большинство объектов наследуются от корневой ткани. Объект. Корневой объект fabric.Object представляет (более или менее) двухмерную фигуру, расположенную в двухмерной плоскости холста. Это объект, у которого есть свойства left / top и width / height, а также множество других графических характеристик. Свойства, перечисленные для объектов — заливка, обводка, угол, непрозрачность, отражение * и т. Д. — являются общими для всех объектов Fabric, которые наследуются от fabric.Object.
Это наследование позволяет вам определять методы на ткани. Объект и разделять их между всеми дочерними «классами». Например, если вы хотите иметь метод getAngleInRadians для всех объектов, вы просто создадите его для fabric.Object.prototype, как показано ниже:
fabric.Object.prototype.getAngleInRadians = function () { вернуть this.getAngle () / 180 * Math.PI; }; var rect = new fabric.Rect ({angle: 45}); rect.getAngleInRadians (); // 0.785 ... var circle = new fabric.Circle ({угол: 30, радиус: 10}); circle.getAngleInRadians (); // 0.523 ... круг экземпляра ткани. Круг; // правда окружность instanceof fabric.Object; // правда
Как видите, метод сразу становится доступным во всех случаях.
Хотя дочерние «классы» наследуются от фабрики. Объекты, они также часто определяют свои собственные методы и свойства. Например, для fabric.Circle необходимо свойство radius, а для fabric.Image, который мы рассмотрим чуть позже, нужны методы getElement и setElement для доступа и установки элемента HTML <img>, из которого происходит экземпляр изображения.
холст
Теперь, когда вы узнали об объектах более подробно, давайте вернемся к холсту.
Первое, что вы видите во всех примерах Fabric, — это создание объекта canvas — new fabric.Canvas (‘…’). Объект fabric.Canvas служит оболочкой для элемента <canvas> и отвечает за управление всеми объектами Fabric на этом конкретном холсте. Он принимает идентификатор элемента и возвращает экземпляр fabric.Canvas.
Вы можете добавлять к нему объекты, ссылаться на них или удалять их, как показано здесь:
var canvas = new fabric.Canvas ('c'); var rect = new fabric.Rect (); canvas.add (прямоугольник); // добавить объект canvas.item (0); // ссылка fabric.Rect добавлена ранее (первый объект) canvas.getObjects (); // получить все объекты на холсте (прямоугольник будет первым и единственным) canvas.remove (прямоугольник); // удалить ранее добавленную fabric.Rect
Управление объектами является основным назначением fabric.Canvas, но также служит хостом конфигурации. Вам нужно установить цвет фона или изображение для всего холста, обрезать все содержимое в определенной области, установить другую ширину и высоту или указать, является ли холст интерактивным или нет? Все эти опции (и другие) могут быть установлены на fabric.Canvas, либо во время создания, либо позже.
var canvas = new fabric.Canvas ('c', { backgroundColor: 'rgb (100,100,200)', selectionColor: 'blue', selectionLineWidth: 2 // ... }); // или var canvas = new fabric.Canvas ('c'); canvas.backgroundImage = 'http: // ...'; canvas.onFpsUpdate = function () {/ * ... * /}; // ...
интерактивность
Одной из уникальных встроенных функций Fabric является слой интерактивности поверх объектной модели. Объектная модель существует, чтобы обеспечить программный доступ и манипулирование объектами на холсте, но снаружи — на уровне пользователя — есть способ манипулировать этими объектами с помощью мыши (или с помощью сенсорных устройств). Как только вы инициализируете холст с помощью нового вызова fabric.Canvas (‘…’), можно выбирать объекты (см. Рисунок 7), перетаскивать их, масштабировать или вращать, и даже группировать их (см. Рисунок 8), чтобы манипулировать ими одним куском!
Рисунок 7 Красный, повернутый прямоугольник в выбранном состоянии (элементы управления видимы)
Рис. 8 Прямоугольник и круг сгруппированы (видимые элементы управления)
Если вы хотите позволить пользователям перетаскивать что-либо на холст, скажем, изображение, все, что вам нужно сделать, это инициализировать холст и добавить к нему объект. Никаких дополнительных настроек или настроек не требуется.
Чтобы контролировать эту интерактивность, вы можете использовать логическое свойство selection Fabric для объекта canvas в сочетании с логическим свойством selectable отдельных объектов:
var canvas = new fabric.Canvas ('c'); ... canvas.selection = false; // отключить выбор группы rect.set («выбираемый», false); // сделать объект невыбираемым
Но что, если вам вообще не нужен слой интерактивности? Если это так, вы всегда можете заменить fabric.Canvas на fabric.StaticCanvas. Синтаксис для инициализации абсолютно одинаков:
var staticCanvas = new fabric.StaticCanvas ('c'); staticCanvas.add ( новая ткань.Rect ({ ширина: 10, высота: 20, слева: 100, сверху: 100, заполнить: «желтый», угол: 30 }));
Это создает «более легкую» версию canvas без какой-либо логики обработки событий. У вас все еще есть вся объектная модель для работы — добавление, удаление или изменение объектов, а также изменение любой конфигурации холста. Все это все еще работает, только обработка событий исчезла.
Позже в этой статье, когда я перейду к пользовательской опции сборки, вы увидите, что если StaticCanvas — это все, что вам нужно, вы даже можете создать более легкую версию Fabric. Это может быть хорошим вариантом, если вам нужно что-то вроде неинтерактивных диаграмм или неинтерактивных изображений с фильтрами в вашем приложении.
Картинки
Добавление прямоугольников и кругов на холст — это весело, но, как вы уже можете себе представить, Fabric также упрощает работу с изображениями. Вот как вы создаете экземпляр объекта fabric.Image и добавляете его на холст, сначала в HTML, а затем в JavaScript:
HTML
<canvas id = "c"> </ canvas> <img src = "my_image.png" id = "my-image">
JavaScript
var canvas = new fabric.Canvas ('c'); var imgElement = document.getElementById ('my-img'); var imgInstance = новая ткань. Изображение (imgElement, { осталось: 100, верх: 100, угол наклона: 30, непрозрачность: 0,85 }); canvas.add (imgInstance);
Обратите внимание, что вы передаете элемент изображения в конструктор fabric.Image. Это создает экземпляр fabric.Image, который выглядит так же, как изображение из документа. Более того, вы сразу устанавливаете значения left / top на 100/100, угол наклона 30 и непрозрачность на 0,85. Как только изображение добавлено на холст, оно отображается в местоположении 100, 100 под углом 30 градусов и слегка прозрачно (см. Рисунок 9). Неплохо!
Рис. 9. Слегка прозрачное и повернутое изображение, выполненное с использованием ткани
Если у вас на самом деле нет изображения в документе, а есть только URL для изображения, вы можете использовать fabric.Image.fromURL:
fabric.Image.fromURL ('my_image.png', function (oImg) { canvas.add (oImg); });
Выглядит довольно просто, не так ли? Просто вызовите fabric.Image.fromURL с URL-адресом изображения и дайте ему обратный вызов для вызова после загрузки и создания изображения. Функция обратного вызова получает уже созданный объект fabric.Image в качестве первого аргумента. В этот момент вы можете добавить его на свой холст или, возможно, сначала изменить его, а затем добавить, как показано здесь:
fabric.Image.fromURL ('my_image.png', function (oImg) { // уменьшаем изображение и переворачиваем его перед добавлением на холст oImg.scale (0,5) .setFlipX (истина); canvas.add (oImg); });
Path и PathGroup
Мы смотрели на простые формы и изображения. А как насчет более сложных, богатых форм и содержания? Познакомьтесь с Path и PathGroup, властной парой.
Контуры в ткани представляют собой контур фигуры, который можно заполнять, обводить и изменять другими способами. Пути состоят из ряда команд, которые по существу имитируют перо, идущее из одной точки в другую. С помощью таких команд, как перемещение, линия, кривая и дуга, траектории могут образовывать невероятно сложные фигуры. А с помощью групп Paths (PathGroup) возможности открываются еще больше. Пути в Fabric очень похожи на элементы SVG <path>. Они используют один и тот же набор команд, могут быть созданы из элементов <path> и могут быть сериализованы в них. Я опишу больше о сериализации и разборе SVG позже, но сейчас стоит упомянуть, что вы, вероятно, будете редко создавать экземпляры Path вручную. Вместо этого вы будете использовать встроенный в SVG синтаксический анализатор Fabric. Но чтобы понять, что такое объекты Path, давайте создадим простой вручную (результаты приведены на рисунке 10):
var canvas = new fabric.Canvas ('c'); var path = new fabric.Path ('M 0 0 L 200 100 L 170 200 z'); path.set ({слева: 120, сверху: 120}); canvas.add (путь);
Рисунок 10. Простой путь, отображаемый с помощью Fabric.
Здесь вы создаете экземпляр объекта fabric.Path и передаете ему строку инструкций пути. Это может выглядеть загадочно, но на самом деле это легко понять. M представляет команду перемещения и сообщает невидимому перу, что нужно перейти к точке 0, 0. L обозначает линию и заставляет перо нарисовать линию к точке 200, 100. Затем другой L создает линию с 170, 200. Наконец, z заставляет перо для рисования закрыть текущий путь и завершить форму.
Поскольку fabric.Path похож на любой другой объект в Fabric, вы также можете изменить некоторые его свойства или изменить его еще больше, как показано здесь и на рисунке 11:
... var path = new fabric.Path ('M 0 0 L 300 100 L 200 300 z'); ... path.set ({fill: 'red', обводка: 'green', непрозрачность: 0,5}); canvas.add (путь);
Рисунок 11. Простой, модифицированный путь.
Из любопытства давайте рассмотрим немного более сложный синтаксис пути. Вы поймете, почему создание путей вручную может быть не самой лучшей идеей:
... var path = new fabric.Path ('M121.32,0L44.58,0C36.67,0,29.5,3.22,24.31,8.41 с-5.19,5.19-8.41,12.37-8.41,20.28c0,15.82,12.87,28.69,28.69,28.69c0,0,4.4, 0,7.48,0C36.66,72.78,8.4,101.04,8.4,101.04C2.98,106.45,0,113.66,0,121.32 c0,7.66,2.98,14.87,8.4,20.29l0,0c5.42,5.42,12.62,8.4,20.28,8.4c7.66,0,14.87 -2.98,20.29-8.4c0,0,28.26-28.25,43.66-43.66c0,3.08,0,7.48,0,7.48c0,15.82, 12.87,28.69,28.69,28.69c7.66,0,14.87-2.99,20.29-8.4c5.42-5.42,8.4-12.62,8.4 -20.28l0-76.74c0-7.66-2.98-14.87-8.4-20.29C136.19,2.98,128.98,0,121.32,0z '); canvas.add (path.set ({слева: 100, сверху: 200}));
Здесь M по-прежнему обозначает команду перемещения, поэтому перо начинает свой путь рисования в точке 121.32, 0. Затем есть команда L (линия), которая приводит перо к 44.58, 0. Пока все хорошо. Теперь идет команда C, которая обозначает «кубический Безье». Эта команда заставляет перо нарисовать кривую Безье от текущей точки до 36,67, 0. Она использует 29,5, 3,22 в качестве контрольной точки в начале линии и 24,31 8,41 в качестве контрольной точки в конце линии. Эта
Затем за всей операцией следуют дюжина других кубических команд Безье, которые в конечном итоге создают красивую форму стрелки, как показано на рисунке 12.
Рисунок 12. Сложный путь, отображаемый с помощью Fabric
Скорее всего, вы не будете работать с такими животными напрямую. Вместо этого вы можете использовать что-то вроде метода fabric.loadSVGFromString или fabric.loadSVGFromURL, чтобы загрузить весь файл SVG и позволить анализатору SVG Fabric выполнить свою работу по обходу всех элементов SVG и созданию соответствующих объектов Path.
В этом контексте, хотя объект Path объекта Fabric обычно представляет элемент SVG <path>, коллекция путей, часто присутствующая в документах SVG, представляется как экземпляр PathGroup (fabric.PathGroup). PathGroup — это не что иное, как группа объектов Path, и поскольку fabric.PathGroup наследует от fabric.Object, его можно добавить на холст, как любой другой объект, и манипулировать таким же образом.
Как и в случае с Paths, вы, вероятно, не будете работать с PathGroup напрямую. Но если вы наткнетесь на один после разбора SVG-документа, вы точно будете знать, что это такое и для какой цели он служит.
Подведение итогов
Я только поцарапал поверхность того, что возможно с Тканью. Теперь вы можете легко создавать любые простые фигуры, сложные фигуры или изображения; добавьте их на холст и измените их по своему усмотрению — их положение, размеры, углы, цвета, обводки, непрозрачность — назовите его.
В следующей статье этой серии я расскажу о работе с группами; анимация; текст; SVG-разбор, рендеринг и сериализация; События; фильтры изображений и многое другое. Тем временем, не стесняйтесь взглянуть на аннотированные демонстрации или тесты , присоединиться к обсуждению в Stack Overflow или перейти прямо к документам , вики и исходным текстам . Вы также можете узнать больше о HTML5 Canvas в Центре разработчиков MSDN IE или ознакомиться с Rey Bango « Введение в элемент HTML 5 Canvas о Script Junkie».
Весело экспериментируйте с тканью! Я надеюсь, вам понравится поездка.
Эта статья была первоначально опубликована по адресу http://msdn.microsoft.com/en-us/magazine/jj856929.aspx и воспроизводится здесь с разрешения.