Pull to refresh

Знакомимся с Fabric.js. Часть 4-я

Reading time 12 min
Views 20K
Это перевод четвертой части серии статей об открытой Javascript canvas библиотеке Fabric.js.

Мы уже многое знаем из предыдущих частей серии: (Ч1, Ч2, Ч3) от простых манипуляций с объектами, до анимаций, событий, групп и подклассов. Но есть еще несколько интересных и полезных вещей, которые стоить осветить.

Свободное рисование


В чем действительно преуспел, так это в отличной поддержке свободного рисования. Canvas - это простой 2D битмап, который олицетворяет собой обычную бумагу. Поэтому свободно рисовать здесь - очень просто и естественно. Разумеется, в Fabric эта способность учтена.

Режим свободного рисования можно включить, просто задав значение true
свойству isDrawingMode на Fabric canvas. С этого момента все клики и движения мыши на canvas будут восприниматься как действия кисти/карандаша.

Вы можете рисовать на canvas сколько пожелаете, пока в свойстве isDrawingMode значение true. Как только вы сделаете любое движение мыши, последующее за событием "mouseup", Fabric инициирует событие "path:created", что трансформирует только что нарисованную форму в настоящий fabric.Path объект.

Если же вы в любой момент времени, вернете свойству isDrawingMode значение false, то все созданные объекты останутся на canvas. Так как это будут объекты fabric.Path их можно модифицировать по своему усмотрению: двигать, крутить, масштабировать и т.д.

Есть два свойства, которые можно настраивать в режиме свободного рисования: freeDrawingBrush.color и freeDrawingBrush.width. Оба этих свойства доступны на объекте freeDrawingBrush, хранящемся на Fabric canvas. freeDrawingBrush.color представляет цвет кисти и может являться любым цветовым значением. freeDrawingBrush.width содержит в себе число пикселей, которое представляет толщину кисти.
image
В ближайшем будущем мы планируем добавить возможность редактирования паттерна для кистей, и добавления собственных паттернов. Наподобие того, как это реализовано с фильтрами у Fabric image.

Кастомизация


Говоря о Fabric, нельзя не отметить ее потрясающие возможности по кастомизации. Вы можете настроить огромное количество опций на canvas, или на объектах canvas, чтобы все работало именно так, как вы захотите. Давайте же приступим к рассмотрению этих опций.

Блокировка объектов


Каждый объект на canvas можно заблокировать различными способами. "lockMovementX", "lockMovementY", "lockRotation", "lockScalingX" и "lockScalingY" – свойства которые блокируют соответствующее действие объекта. Если задать свойству object.lockMovementX значение true, то это запретит двигать объект горизонтально. Возможность же вертикального движения при этом не изменится. Таким же образом lockRotation блокирует вращение, а lockScalingX/lockScalingY — масштабирование. Все эти свойства независимы друг от друга, их можно использовать вместе или комбинировать по своему усмотрению.

Изменение рамок, уголков


Вы можете изменить видимость рамок и уголков на объекте, используя свойства "hasControls" и "hasBorders". Если поставить им значение false, то объект отрисуется "голым".
object.hasBorders = false;

image
object.hasControls = false;

image
Также вы можете настроить внешнее представление управляющих элементов, изменяя свойства "borderColor", "cornerColor", и "cornerSize".
object.set({
  borderColor: 'red',
  cornerColor: 'green',
  cornerSize: 6
});

image

Отмена выделения


Вы можете отменить выделение объектов, задав свойству "selection" на canvas значение false. Это предотвратит выделение на всему, что представлено на canvas. Если же вам нужно отменить выделение на конкретном объекте, то следует задать в его свойство "selectable" значение false. Тем самым объект потеряет свою интерактивность.

Кастомизация выделения


А если вам нужно не отменить выделение, а изменить его внешнее представление? Никаких проблем.
Есть 4 свойства на canvas, которые за это отвечают - "selectionColor", "selectionBorderColor", "selectionLineWidth", и "selectionDashArray". Их предназначение, в целом, должно быть понятно по названию. Обратим внимание на пример:
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));

canvas.selectionColor = 'rgba(0,255,0,0.3)';
canvas.selectionBorderColor = 'red';
canvas.selectionLineWidth = 5;

image
Последнее свойство - "selectionDashArray" – не такое простое. Оно позволяет нам сделать линии выделения пунктирными. Мы определяем пунктирный паттерн, используя интервалы в массиве. Чтобы создать паттерн, в котором длинная черта сменяет короткую, мы будем использовать что-то вроде этого [10, 5] в качестве массива "selectionDashArray". При таком массиве нарисуется линия длиной в 10px, затем будет отступ в 5px, снова линия, и так далее. Если мы используем массив [2, 4, 6], то паттерн создастся следующим образом: сначала нарисуется черта размером в 2px, затем отступ в 4px, после этого снова черта, но уже размером в 6px и так далее. В качестве примера посмотрите, как выглядит паттерн с массивом [5, 10]:
image

Пунктирная обводка


Схоже с "selectionDashArray" свойством на canvas, все Fabric объекты имеют свойство "strokeDashArray", отвечающее за пунктирный паттерн на обводке объекта.
var rect = new fabric.Rect({
  fill: '#06538e',
  width: 125,
  height: 125,
  stroke: 'red',
  strokeDashArray: [5, 5]
});
canvas.add(rect);

image

Кликабельная зона


Как вы уже знаете, все Fabric объекты имеют ограничительный контейнер, который используется для перемещения, масштабирования и вращения объекта, в том случае если рамки/уголки присутствуют. Вы могли заметить, что можно переместить объект, нажимая на внутреннее пространство ограничительного контейнера, даже если оно пустое. Посмотрим на изображение ниже:


При стандартных настройках все Fabric объекты на canvas можно перемещать при помощи ограничительного контейнера. Однако, если вы хотите иного поведения, а именно: работать непосредственно с объектом, то нужно в свойство "perPixelTargetFind" на объекте записать значение true.

Вращающий элемент


Начиная с версии 1.0 Fabric использует альтернативное UI. Теперь, при изначальных настройках, объекты нельзя одновременно крутить и масштабировать. Вместо этого появились отдельные вращающие контроллеры на каждом из объектов. Свойство, отвечающее за этот вращающий элемент - "hasRotatingPoint". Вы можете численно задать размер этого элемента через свойство "rotatingPointOffset".
image

Трансформация объекта


Также, начиная с версии 1.0, существует ряд других свойств, отвечающих за трансформацию объекта. Одно из них - "uniScaleTransform" на canvas. При начальных настройках, в этом свойстве значение false. Оно инициирует неравномерное масштабирование объекта. Иными словами, свойство позволяет изменять пропорции, перетаскивая за уголки.
image
Помимо этого есть свойства "centeredScaling" и "centeredRotation" (До версии 1.3.4 было одно свойство - "centerTransform"). Они определяют, должна ли трансформация осуществляться относительно центра объекта. Когда оба они true, возвращается поведение, которое было до версии 1.0, когда объекты всегда масштабировались/вращались относительно своего центра. Начиная с версии 1.0. такое поведение стало настраиваемым.

Последняя пара новых свойств это "originX" и "originY". Изначально в них содержаться стандартные значения соответственно "left" и "top". Они позволяют программно изменить относительность трансформации объекта. При перетаскивании объекта за уголки, именно эти свойства изменяются динамически, "под капотом".

Когда же нам может понадобиться изменить эти свойства вручную? Например, работая с текст-объектами. Когда вы динамически изменяете текст, и его контейнер увеличивается, "originX" и "originY" указывает куда расти этому контейнеру. Если вам нужно отцентрировать текст-объект, то следует поставить свойству originX значение "center". Чтобы прибить текст к правому краю – ставим originX значение "right" и т.д. Такое поведение похоже на "position: absolute" в CSS.

Задний фон и перекрытие на canvas


Как вы, вероятно, помните из 1-й части серии, существует возможность определить цвет для заполнения всего заднего фона canvas. Для этого необходимо просто задать любое цветовое значение в его свойство "backgroundColor".
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
canvas.backgroundColor = 'rgba(0,0,255,0.3)';
canvas.renderAll();

image

Можно пойти еще дальше, и сделать задним фоном изображение. Для этого нужно использовать метод setBackgroundImage, добавив url и коллбэк на завершение загрузки изображения.
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
canvas.setBackgroundImage('../assets/pug.jpg', canvas.renderAll.bind(canvas));

image

Наконец, вы также можете использовать изображение в качестве перекрытия. В этом случае оно появится поверх всех объектов canvas. Просто используйте setOverlayImage, с теми же опциями: url, и коллбэком на завершение загрузки изображения.
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
canvas.setOverlayImage('../assets/jail_cell_bars.png', canvas.renderAll.bind(canvas));

image

Fabric на Node.js


Один из уникальных аспектов Fabric – это то, что с ним можно работать не только на клиенте в браузере, но и на сервере! Это может быть полезно, когда вы хотите послать данные от клиента и создать изображение из этих данных прямо на сервере. Или вы просто хотите использовать Fabric API из консоли по причине скорости, производительности или же по любой другой причине.

Давайте разберемся, как нам настроить сервер Node, чтобы начать использовать на нем Fabric.
Для начала, вам нужно установить Node.js, если он у вас еще не установлен. Есть несколько способов это сделать, они зависят от платформы, которую вы используете. Вы можете следовать этим или этим инструкциям.

Как только установите Node.js, вам необходимо установить библиотеку node-canvas. Эта библиотека – реализация canvas для NodeJS. Она основывается на Cairo — 2D графической библиотеке, работающей на Mac, Linux, или Windows. node-canvas имеет различные инструкции по установке, которые зависят от типа платформы.

Fabric представлен на Node в качестве пакетного менеджера (NPM). Поэтому следующий шаг – установить NPM. Вы можете найти инструкции в репозитории github.

Последний шаг – это установка Fabric package, используя NPM. Это можно сделать, запустив npm install fabric (или npm install -g fabric для глобальной установки). К этому моменту, запустив консоль, у нас должны работать и node-canvas и Fabric:
> node
...
> typeof require('canvas'); // "function"
> typeof require('fabric'); // "object"

Теперь все готово, можем попытаться сделать небольшой "hello world" тест. Создадим helloworld.js файл.
var fs = require('fs'),
    fabric = require('fabric').fabric,
    out = fs.createWriteStream(__dirname + '/helloworld.png');

var canvas = fabric.createCanvasForNode(200, 200);
var text = new fabric.Text('Hello world', {
  left: 100,
  top: 100,
  fill: '#f55',
  angle: 15
});
canvas.add(text);

var stream = canvas.createPNGStream();
stream.on('data', function(chunk) {
  out.write(chunk);
});

Теперь запустим это в качестве node helloworld.js. Открывающееся изображение helloworld.png выдаст следующее:
image
Что здесь происходит? Давайте детально разберем этот код.

Сначала мы подключили Fabric (fabric = require('fabric').fabric) . Затем мы создали старый-добрый Fabric canvas, использовав fabric.createCanvasForNode, вместо обычного new fabric.Canvas(). Этот метод принимает ширину и высоту в качестве параметров, и создает canvas с этими размерами (в данном случае 200х200).

Далее уже знакомым способом создаем текстовый объект (new fabric.Text()) и добавляем на canvas (canvas.add(text)).

Все это создаст Fabric canvas, и нарисует на нем текст-объект. Теперь, как нам создать изображение из всего содержимого canvas? Нам нужно использовать метод createPNGStream на объекте canvas. Этот метод вернет нам типичный для Node stream объект, который можно использовать как текстовое представление для файла изображения. Для этого нужно использовать on('data'), и (fs.createWriteStream()), что на выходе даст нам файл изображения.

fabric.createCanvasForNode и fabric.Canvas#createPNGStream – единственные два метода использующиеся только на Node. Все остальные работают по-прежнему: вы можете как обычно создавать объекты, модифицировать их, отрисовывать и т.д. Стоит отметить, что когда вы создаете canvas при помощи метода createCanvasForNode, canvas расширяется свойством nodeCanvas, которое является ссылкой на оригинальный node-canvas объект.

Node сервер и Fabric


В качестве примера, создадим простой Node сервер, который будет принимать входящие запросы в качестве данных Fabric в JSON формате, и отсылать изображение из этих данных. Весь код займет всего 25 строк!
var fabric = require('fabric').fabric,
    http = require('http'),
    url = require('url'),
    PORT = 8124;

var server = http.createServer(function (request, response) {
  var params = url.parse(request.url, true);
  var canvas = fabric.createCanvasForNode(200, 200);

  response.writeHead(200, { 'Content-Type': 'image/png' });

  canvas.loadFromJSON(params.query.data, function() {
    canvas.renderAll();

    var stream = canvas.createPNGStream();
    stream.on('data', function(chunk) {
      response.write(chunk);
    });
    stream.on('end', function() {
      response.end();
    });
  });
});

server.listen(PORT);

Большая часть кода из этого отрывка должна быть уже знакомой. Основная суть содержится в ответе сервера. Мы создаем Fabric canvas, загружаем на него JSON данные, отрисовываем и транслируем конечный результат в качестве ответа сервера.

Чтобы это протестировать, возьмем данные зеленого прямоугольника с небольшим углом поворота.
{"objects":[{"type":"rect","left":103.85,"top":98.85,"width":50,"height":50,"fill":"#9ae759","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1.39,"scaleY":1.39,"angle":30,"flipX":false,"flipY":false,"opacity":0.8,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0}],"background":"rgba(0, 0, 0, 0)"}

URI закодирует это:
%7B"objects"%3A%5B%7B"type"%3A"rect"%2C"left"%3A103.85%2C"top"%3A98.85%2C"width"%3A50%2C"height"%3A50%2C"fill"%3A"%239ae759"%2C"overlayFill"%3Anull%2C"stroke"%3Anull%2C"strokeWidth"%3A1%2C"strokeDashArray"%3Anull%2C"scaleX"%3A1.39%2C"scaleY"%3A1.39%2C"angle"%3A30%2C"flipX"%3Afalse%2C"flipY"%3Afalse%2C"opacity"%3A0.8%2C"selectable"%3Atrue%2C"hasControls"%3Atrue%2C"hasBorders"%3Atrue%2C"hasRotatingPoint"%3Afalse%2C"transparentCorners"%3Atrue%2C"perPixelTargetFind"%3Afalse%2C"rx"%3A0%2C"ry"%3A0%7D%5D%2C"background"%3A"rgba(0%2C%200%2C%200%2C%200)"%7D

Отправим на сервер в качестве параметра "data". Немедленный ответ с типом контента "image/png" выглядит вот так:
image

Как вы видите, работать с Fabric на сервере довольно просто. Попробуйте поэкспериментировать с этим примером. Может быть, поменять размеры canvas, установив другие значения в параметры, или же изменить данные перед возвращением изображения в качестве ответа.

Шрифты в Fabric на Node


Перед тем как использовать шрифты на Fabric, нужно их сперва загрузить. Самый распространенный способ загрузить шрифты в браузере (клиентская сторона) – это использовать CSS3 @font-face rule. В Fabric на Node (серверная сторона) мы можем использовать node-canvas Font API, с помощью чего можно загрузить шрифты с невероятной легкостью.

Пример ниже демонстрирует, как загрузить и использовать шрифты. Сохраните его в файл customfont.js , убедившись, что пути до файлов шрифтов корректны. В данном примере в качестве шрифта используется Ubuntu.
var fs = require('fs'),
    fabric = require('fabric').fabric,
    canvas = fabric.createCanvasForNode(300, 250);

var font = new canvas.Font('Ubuntu', __dirname + '/fonts/Ubuntu-Regular.ttf');
font.addFace(__dirname + '/fonts/Ubuntu-Bold.ttf', 'bold');
font.addFace(__dirname + '/fonts/Ubuntu-Italic.ttf', 'normal', 'italic');
font.addFace(__dirname + '/fonts/Ubuntu-BoldItalic.ttf', 'bold', 'italic');

canvas.contextContainer.addFont(font);  // для createPNGStream или createJPEGStream
//canvas.contextTop.addFont(font);      // для toDataURL или toDataURLWithMultiplier

var text = new fabric.Text('regular', {
    left: 150,
    top: 50,
    fontFamily: 'Ubuntu'
});
canvas.add(text);

text = new fabric.Text('bold', {
    left: 150,
    top: 100,
    fontFamily: 'Ubuntu',
    fontWeight: 'bold'
});
canvas.add(text);

text = new fabric.Text('italic', {
    left: 150,
    top: 150,
    fontFamily: 'Ubuntu',
    fontStyle: 'italic'
});
canvas.add(text);

text = new fabric.Text('bold italic', {
    left: 150,
    top: 200,
    fontFamily: 'Ubuntu',
    fontWeight: 'bold',
    fontStyle: 'italic'
});
canvas.add(text);

var out = fs.createWriteStream(__dirname + '/customfont.png');
var stream = canvas.createPNGStream();
stream.on('data', function(chunk) {
    out.write(chunk);
});

Запустив пример, используя node customfont.js, создастся изображение (customfont.png) которое выглядит так:
image

Давайте детально рассмотрим что здесь происходит. Во-первых мы создали объект шрифта, используя new canvas.Font(), указав имя (name) и путь (path) к файлу со шрифтом в качестве параметров. Затем добавляем дополнительные font-faces, используя метод font.addFace() . В качестве параметров используем путь (path), насыщенность (weight) и стиль (style). Наконец, шрифт полностью готов к добавлению. Мы его добавляем, используя canvas.contextContainer.addFont() (для createPNGStream или createJPEGStream) , или canvas.contextTop.addFont() (для toDataURL или toDataURLWithMultiplier).

Теперь мы можем использовать наш шрифт, задав свойству fontFamily на fabric.Text объекте название шрифта. В комбинации со свойствами fontWeight и fontStyle мы можем применять font faces, которые ранее добавили. Подробнее об этих свойствах во 2-й части серии.

Обратите внимание, что пример иллюстрирует, как использовать шрифты, создавая новые текст-объекты, но также можно использовать текст-объекты, загруженные из JSON.

На этом заканчивается серия их четырех частей описывающих Fabric. Я надеюсь, вы получили достаточно знаний, чтобы создать что-то интересное, крутое, полезное, захватывающее, вызывающее!

оригинал статьи.
Tags:
Hubs:
+9
Comments 0
Comments Leave a comment

Articles