вторник, 21 февраля 2017 г.

Введение в GraphicsJS



HTML это основа современного веба. И сегодня, когда пришло время интерактивных изображений, SVG и Canvas часто являются предметом выбора – Flash уже забыт, Silverlight – редкий единорог который обитает на окраине Веба, ну и путь еще есть парочку небольших сторонних библотек.
Минусы и плюсы каждого из них хорошо документированы, но по сути, SVG лучше подходит для создания и обработки интеракивных элементов. Это связано с тем, что SVG описывается векторным XML-форматом, и когда изображение загружено на странице с использованием тэга <svg>, каждый элемент в нем становится полноценным элементов SVG DOM.
В данной статье, я представлю вам GraphicsJS, новую и мощную JavaScript библиотеку для рисования с открытым исходным кодом, которая базируется на SVG (с поддержкой VML для старых версий IE). Я начну с короткого введения, в котором расскажу о базовых принципах, а после этого продемонстрирую функциональность библиотеки с помощью двух коротких, но тем не менее впечатляющих примеров: первый – это исскуство, в то время как второй иллюстрирует как программно создавать игру убийцу времени менее чем за 50 строк кода. 


Почему GraphicsJS



Существует много библиотек, которые предназначены для работы с SVG: Raphaël, Snap.svg, и BonsaiJS это одни из лучших. Каждая из них имеет свои преимущства и недостатки, но сравнивать их в данной статье я не буду. Вместо этого я опишу GraphicsJS, поэтому давайте я объясню что делает библиотеку хорошей и особенной.
Во первых, GraphicsJS легковесная и имеет очень гибкое JavaScript API. Оно реализует большое разнообразие логики для работы с текстом, имеет отличный виртуальный DOM – отделенный от HTML DOM реализации конкретного браузера.
Во-вторых, это новая библиотека JavaScript с открытым исходным кодом, которую совсем недавно выпустил AnyChart, один из лучших разработчиков в сфере визуализации интерактивных данных. Данный разработчик использовал GaphicsJS для отображения диаграмм в своих коммерческих продуктах на протяжении как минимум последних трех лет (со времен появления AnyChart 7.0). Таким образом, данная библиотека хорошо протестирована в реальных условиях (Отречение: я ни в коем случае не являюсь ни R&D в AnyChart ни главный разработчик GaphicsJS).
В-третьих, в отличие от прочих библиотек AnyChart, библиотека GraphicsJS может использоваться бесплатно в коммерческих и некомерческих проектах. Она доступна в GitHub под лицензией Apache.
В четвертых, работает с любыми браузерами, и поддерживает IE 6.0+, Safari 3.0+, Firefox 3.0+ и Opera 9.5+. Она использует VML в старых версиях IE и SVG во всех остальных браузерах.
Наконец, GraphicsJS позволяет комбинировать графику и анимацию для получения лучшего эффекта. Посмотрите  на главную галерею, которая демонстрирует возможности анимационного костра, вращающейся галактики, падающего дождя, процедуры генерации листьев, игры пятниашки и многое другое. GraphicsJS содержит много других примеров, всеобъемлющей документации и справочник по API.

Основы GraphicsJS

Для того что бы начать использовать библиотеку нужно добваить на ссылку на нее и создать блочный элемент HTML для рисования:

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>GraphicsJS Basic Example</title>    
  </head>
  <body>
    <div id="stage-container" style="width: 400px; height: 375px;"></div>
 
    <script src="https://cdn.anychart.com/js/latest/graphics.min.js"></script>
    <script>
      // GraphicsJS code here
    </script>
  </body>
&lt;/html&gt;

После этого нужно создать стейдж и нарисовать что-нибудь, например прямоугольник или круг или другую фигуру:

// create a stage
var stage = acgraph.create('stage-container');
// draw a rectangle
var stage.rect(25, 50, 350, 300);
Здесь представлен пример CodePen в котором мы идем немного дальше и рисуем символ Дары смерти.

Наш Первый Шедевр

Fill, Stroke и текстурная заливка

Любая фигура может быть разукрашена с использованием fill settings и stroke settings. Любой объект имеет границу, но только фигуры и закрытые контуры имеют заливку. Настройки контуров и заливки очень разнообразны, вы можете использовать даже линейный и круговой градиенты и для контуров и для заливок. Кроме того линии могут быть прерывистыми, заливка может базироваться на некотором изображении с использованием наскольких плиточных режимов. Но все это весьма стандартно, что можно найти и в других библиотеках.Но есть функциональность которая делает GraphicsJS уникальной библиотекой – это штриховка и узор заливки, которая позволяет не только использовать доступные 32 вида штриховок, но также и легко создавать свои новые виды.
Теперь давайте посмотрим как именно это делается! Мы нарисуем мальенкую картинку с человечком стоящим возле дома, и после этого улучшим эту картику с помощью различных видов заливки. Для простоты картинку сделаем немного наивной (попытаемся не попасть в арт-брют). 

// create a stage
var stage = acgraph.create('stage-container');
 
// draw the frame
var frame = stage.rect(25, 50, 350, 300);
 
// draw the house
var walls = stage.rect(50, 250, 200, 100);
var roof  = stage.path()
  .moveTo(50, 250)
  .lineTo(150, 180)
  .lineTo(250, 250)
  .close();
 
// draw a man
var head = stage.circle(330, 280, 10);
var neck = stage.path().moveTo(330, 290).lineTo(330, 300);
var kilt = stage.triangleUp(330, 320, 20);
var rightLeg = stage.path().moveTo(320, 330).lineTo(320, 340);
var leftLeg = stage.path().moveTo(340, 330).lineTo(340, 340);


Как вы заметили мы используем переменные – все методы которые что-то рисуют на стейдже возвращают ссылку на созданный объект, и эта ссылка может быть использована для изменения или удаления объекта.
Также, обратите внимание на то как формируется цепочка, которая в GraphicsJS используется повсеместно и помоает сократить код. Формирование цепочки вызовов (например stage.path().moveTo(320, 330).lineTo(320, 340);) следует использовать аккуратно, но оно делает код более компактным и легким в понимании.
Теперь давайте эту разукрашку передадим ребенку и позволим ему ее разукрасить. Потому что даже ребенок способен совладать с подобной техникой:
// color the picture
// fancy frame
frame.stroke(["red", "green", "blue"], 2, "2 2 2");
// brick walls
walls.fill(acgraph.hatchFill('horizontalbrick'));
// straw roof
roof.fill("#e4d96f");
// plaid kilt
kilt.fill(acgraph.hatchFill('plaid'));


Далее возмем картику с горцем в килте, котрый стоит возле каменного замка с соломенной крышей. Мы можем ударится в крайности и сказать что это действительно произведение искусства, авторские права на который мы хотим защитить. Давайте это сделаем с кастомным паттерном заливки постреном в виде текста.

// 169 is a char code of the copyright symbol
var  text = acgraph.text().text(String.fromCharCode(169)).opacity(0.2);
var  pattern_font = stage.pattern(text.getBounds());
pattern_font.addChild(text);
// fill the whole image with the pattern
frame.fill(pattern_font);

Как вы видите это очень просто: создаете экземпляр текстового объекта, после формируете паттерн на стедже и добавляете текст в паттерн.

Создание художественной игры Убийцы-времени за менее чем 50 строк кода

В следующей части статьи, я  хочу показать как создать Кликер Печенек – игру на GraphicsJS за менее чем 50 строк кода.
Название игры “Дворник на ветру” – и игрок становится дворником в ветренную погоду. В игре используется код с примера с падающими листьями с галереи GraphicsJS.
Вы можете посмотреть на завершенную игру здесь (или дочитать до конца статью).

Слои, zIndex и Виртуальный DOM

Мы начнем с создания стейджа ( как и раньше), потом объявим несколько переменных:
// create stage
var stage = acgraph.create("stage-container");
 
// color palettes for leaves
var palette_fill = ['#5f8c3f', '#cb9226', '#515523', '#f2ad33', '#8b0f01']; 
var palette_stroke = ['#43622c', '#8e661b', '#393b19', '#a97924', '#610b01'];
 
// counter
var leavesCounter = 0;

Для этой игры мы будем использовать Слой – объект, предназначенный для групировки элементов в GraphicsJS. Элементы должны группироваться если вы хотите применить одни и теже изменения для них, например трансформацию. Вы можете изменить слои когда находитесь в приостановленном режиме (подробнее об этом чуть позже),  который увеличивает производительность и пользовательский интерфейс.
В этом демо мы используем функциональность слоев для того что бы сгруппировать листья и иизбежать их перекрытия метками (которые показывают, сколько было обработано). Для того что бы это сделать мы создали метку и после этого вызвали метод stage.layer, который привязывает слой к стейджу. Мы присвоили этому слою более низкий zIndex, чем у метки.

// create a label to count leaves
var counterLabel = stage.text(10,10, "Swiped: 0", {fontSize: 20});
 
// a layer for the leaves
var gameLayer = stage.layer().zIndex(counterLabel.zIndex()-1);
 

После этого, нет большой разницы сколько листьев мы создаем на сслое, они в любом случае не смогут перекрыть метку.

Трансформации

Далее, давайте добавим функциою рисования листьев. Это позволит нам познакомится с API для трансформаций в GraphicsJS, которое позволяет двигать, масштабировать, поворачивать и сдвигать и элементы и группы элементов. Когда трансформации используются со слоями и витруальным DOM – это становиться очень мощным инструментом.
function drawLeaf(x, y) {
  // choose a random color from a palette
  var index = Math.floor(Math.random() * 5);
  var fill = palette_fill[index];
  var stroke = palette_stroke[index];
 
  // generate random scaling factor and rotation angle
  var scale = Math.round(Math.random() * 30) / 10 + 1;
  var angle = Math.round(Math.random() * 360 * 100) / 100;
 
  // create a new path (leaf)
  var path = acgraph.path();
 
  // color and draw a leaf
  path.fill(fill).stroke(stroke, 1, 'none', 'round', 'round');
  var size = 18;
  path.moveTo(x, y)
    .curveTo(x + size / 2, y - size / 2, x + 3 * size / 4, y + size / 4, x + size, y)
    .curveTo(x + 3 * size / 4, y + size / 3, x + size / 3, y + size / 3, x, y);
 
  // apply random transformations
  path.scale(scale, scale, x, y).rotate(angle, x, y);
 
  return path; 
};

Вы видите, что любой контур создается одинаковым образом, но после этого подвергается трансформации. В результате получается очень интересный рандомный паттерн для листьев.

Обработка событий

Любой объект, стейдж или слой в GraphicsJS может обрабатывать события. Полный список поддерживаемых событий доступен в EventType API. У стейджа есть четыре специальные события для контроля отрисовки.
В данном примере мы используем слушатели событий, которые подсоеденены к объектам листьев. Таким образом исчезают один за одним, когда пользователь наводит курсор мыши. Для того, что бы этого добиться, добавте следующий код в конец функции drawLeaves перед вызовом return.

path.listen("mouseover", function(){
  path.remove();
  counterLabel.text("Swiped: " + leavesCounter++);
  if (gameLayer.numChildren() < 200) shakeTree(300); 
});
Here we can also see that we are using the layer to count leaves.
if (gameLayer.numChildren() < 200) shakeTree(300); 


Обратите внимание, что мы не храним общее количество листьев. Поскольку листья – это контуры, которые добавляются и удаляются с конкретного слоя, то это позволяет отслеживать сколько дочерних элементов он имеет (и сколько листьев осталось).
GraphicsJS предоставляет virtual DOM,абстракцию HTML DOM, легковесную и не связанную с реализаций SVG\VML конкретного браузера. Это полезно для того что бы делать много клевых вещей, таких как отслеживание всех объектов на слое, применения трансформаций для групп и оптимизации отрисовки с помощью методов, которые позволяют нам отслеживать и контролировать процесс отрисовки.

Оптимизация производительности

Витруальный DOM вместе с обработчиками событий позволяют пользовтелям GrasphicsJS контролировать отрисовку. Статья по производительности может дать вам идею как это можно применять.
При генерации листьев в нашей игре, мы должны приостановить отрисовку когда добавляем новые листья и возобновить после того как наши изменения были сделаны.
function shakeTree(n){
  stage.suspend(); // suspend rendering
  for (var i = 0; i < n; i++) {
    var x = Math.random() * stage.width()/2 + 50;
    var y = Math.random() * stage.height()/2 + 50;
    gameLayer.addChild(drawLeaf(x, y)); // add a leaf
  }
 
  stage.resume(); // resume rendering
}
 

Благодаря такому способу, листья появляются почти мгновенно.
Наконец, после того как все сделано, вызовем shakeTree().

// shake a tree for the first time
shakeTree(500);

Выводы

Движение к HTML5 изменило веб. Когда речь заходит о современных веб приложениях или даже о простых сайтах, мы часто сталкиваемся с задачми по работе с изображениями. Невозможно найти решение которое работает одинаково хорошо в различных ситуациях, но мы должны присмотреться к GraphicsJS. Это библиотека с отрытым исходным кодом, быстрая с изумительной поддержкой браузеров и большим количеством различных возможностей, которые делают ее интересной, удобной и разумеется полезной.
Я буду рад услышать любые отзывы о библиотеке в коментариях к статье. Вы используете ее уже? А будете использовать для новых проектов? Я был бы рад услышать почему да или почему нет. В настоящий момент я работаю над списком JavaScript библиотек для рисования и статьей, в которой буду сравнивать их. Поэтому сообщайте мне обо всех библиотеках, которые вы хотите увидеть в моем обзоре.
Источник: https://www.sitepoint.com/introducing-graphicsjs-a-powerful-lightweight-graphics-library/