- PVSM.RU - https://www.pvsm.ru -

GraphicsJS – графическая JavaScript библиотека

GraphicsJS – графическая JavaScript библиотека - 1

Перевод статьи. Англоязычный оригинал опубликован на SitePoint – "Introducing GraphicsJS, a Powerful Lightweight Graphics Library [1]".

HTML5 – основа основ современного веба. И сегодня, когда встает задача создать интерактивную графику, выбор чаще всего падает на такие технологии, как SVG и Canvas. Flash позабыт, Silverlight – редкая птица, обитающая на задворках веба, и почти никто не помнит сторонние ActiveX и Java-плагины.

Плюсы и минусы SVG и Canvas хорошо известны [2] – в целом все сводится к тому, что для создания интерактивных элементов и работы с ними больше подходит SVG. Это векторный формат, основанный на XML, и, когда изображение загружается на страницу с использованием тега <svg>, каждый его внутренний элемент становится доступен в SVG DOM.

В данной статье я хочу рассказать о GraphicsJS [3]. Это мощная графическая JavaScript-библиотека с открытым исходным кодом, основанная на технологии SVG (VML [4] для старых версий IE). Начну с краткого введения в основы GraphicsJS, а затем проиллюстрирую возможности библиотеки двумя небольшими, но наглядными примерами. Первый из них посвящен изобразительному искусству. Второй покажет, как менее чем за 50 строк кода сделать простую арт-игру в жанре таймкиллера.

Почему GraphicsJS

Библиотек, облегчающих работу с SVG, довольно много: в число лучших входят Raphaël [5], Snap.svg [6] и BonsaiJS [7]. У каждой есть сильные и слабые стороны, однако их детальное сравнение будет темой одной из следующих публикаций. Данная же статья – исключительно о GraphicsJS, и сейчас я попытаюсь объяснить, чем эта библиотека хороша и выделяется среди прочих.

Во-первых, GraphicsJS весит совсем немного и обладает очень гибким JavaScript API. Она предоставляет богатые возможности для форматирования текста, а также виртуальный DOM – независимый от специфики HTML DOM в разных браузерах.

Во-вторых, код этой библиотеки был открыт только прошлой осенью [8]. Компания AnyChart [9], один из лидеров в разработке решений для интерактивной визуализации данных, использует GraphicsJS как графический движок в своих коммерческих продуктах уже порядка трех лет (с момента выхода AnyChart 7.0), так что эта библиотека проверена в боевых условиях. (Дисклеймер: я руководитель R&D в AnyChart и ведущий разработчик GraphicsJS.)

В-третьих, в отличие от других продуктов AnyChart – JavaScript-библиотек для построения графиков – GraphicsJS бесплатна для использования как в коммерческих, так и в некоммерческих целях. Библиотека доступна на GitHub [10] под лицензией Apache.

В-четвертых, GraphicsJS обладает кросс-браузерной совместимостью, включая поддержку Internet Explorer 6.0+, Safari 3.0+, Firefox 3.0+, Opera 9.5+. В старых версиях IE библиотека использует VML, во всех остальных браузерах – SVG.

Наконец, GraphicsJS позволяет эффективно комбинировать графику и анимацию. Посмотрите главную галерею [3] библиотеки: здесь есть такие примеры, как пылающий костер [11], вращающаяся галактика [12], дождь [13], процедурно генерируемые листья [14], головоломка «Пятнашки» [15] и так далее. В развернутой документации [16] и детальном описании API [17] можно найти еще больше примеров использования GraphicsJS.

Основы GraphicsJS

Для начала работы с 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
    </script>
  </body>
</html>

Затем надо построить рабочую область и что-нибудь в ней нарисовать, например прямоугольник [18], круг [19] или какую-то другую фигуру [20]:

// создаем рабочую область
var stage = acgraph.create('stage-container');
// рисуем прямоугольник
var stage.rect(25, 50, 350, 300);

Вот пример на CodePen [21], в котором мы идем чуть дальше и рисуем знак Даров Смерти [22].

Наш первый шедевр

Контур, заливка цветом и узором

Любую фигуру или линию можно раскрашивать, используя настройки заливки [23] и контура [24]. Контур (граница) есть у всего, но заливка доступна только для фигур и замкнутых линий.

GraphicsJS предлагает многочисленные настройки этих параметров, вплоть до линейного и радиального градиента, причем и в заливке, и в контуре. Линии могут быть пунктирными, а также поддерживается заливка изображением с несколькими режимами замощения. Однако такой набор функций можно найти практически в любой библиотеке. Что отличает GraphicsJS – так это опция заливки штриховкой и узором [25], которая позволяет не только выбирать из тридцати двух (!) готовых вариантов заполнения [26], но и легко создавать собственные паттерны из фигур или текста.

А теперь посмотрим, что конкретно можно сделать. Я нарисую небольшую картинку (человечек, стоящий возле дома) и затем доработаю ее с помощью заливки цветом и узорами. Пусть для простоты это будет изображение в стиле наивного искусства [27] (постараюсь не докатиться до ар брют [28]). Поехали:

// создаем рабочую область
var stage = acgraph.create('stage-container');

// рисуем рамку
var frame = stage.rect(25, 50, 350, 300);

// рисуем дом
var walls = stage.rect(50, 250, 200, 100);
var roof  = stage.path()
  .moveTo(50, 250)
  .lineTo(150, 180)
  .lineTo(250, 250)
  .close();

// рисуем человечка
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);

Результат можно посмотреть на CodePen [29].

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

Кроме того, в GraphicsJS можно активно применять цепные вызовы (например, stage.path().moveTo(320, 330).lineTo(320, 340);), что позволяет сократить код. Использовать эту возможность нужно аккуратно, но при правильном подходе цепной вызов действительно делает код более компактным и легко читаемым.

Теперь можно дать получившуюся у нас картинку какому-нибудь ребенку и попросить раскрасить ее – ведь даже ребенок способен освоить следующий прием:

// раскрашиваем картинку
// элегантная рамка
frame.stroke(["red", "green", "blue"], 2, "2 2 2");
// кирпичные стены
walls.fill(acgraph.hatchFill('horizontalbrick'));
// соломенная крыша
roof.fill("#e4d96f");
// клетчатый килт
kilt.fill(acgraph.hatchFill('plaid'));

Теперь наш пример выглядит вот так [30].

Сейчас на картинке изображен горец в килте. Он стоит возле своего кирпичного замка с соломенной крышей. Можно даже рискнуть и действительно назвать нашу картинку произведением искусства, авторские права на которое мы хотим защитить. Что ж, проделаем это с помощью оригинальной заливки текстовым паттерном, которую мы сами и настроим:

// 169 - символьный код значка копирайта
var  text = acgraph.text().text(String.fromCharCode(169)).opacity(0.2);
var  pattern_font = stage.pattern(text.getBounds());
pattern_font.addChild(text);
// заполняем паттерном все изображение
frame.fill(pattern_font);

Как видите, это очень просто: нужно создать экземпляр текстового объекта [31], затем построить паттерн [32] в рабочей области и, наконец, вставить текст в паттерн.

Рисуем арт-игру (таймкиллер) менее чем за 50 строк кода

В следующей части статьи я покажу, как с помощью GraphicsJS нарисовать игру-кликер типа Cookie Clicker [33] меньше чем за 50 строк кода.

Название этой игры – «Дворник на ветру». В ней пользователь выступает в роли дворника, который подметает улицу ветреным осенним днем. Здесь частично используется код примера с процедурно генерируемыми листьями [14] из галереи GraphicsJS.

Финальный вариант игры можно посмотреть на CodePen [34] (или в конце этой статьи).

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

Начнем с создания рабочей области (как в предыдущем примере). И затем объявим несколько исходных переменных:

// создаем рабочую область
var stage = acgraph.create("stage-container");

// цветовые палитры для листьев
var palette_fill = ['#5f8c3f', '#cb9226', '#515523', '#f2ad33', '#8b0f01']; 
var palette_stroke = ['#43622c', '#8e661b', '#393b19', '#a97924', '#610b01'];

// счетчик
var leavesCounter = 0;

Для создания игры нам пригодится возможность работы со слоем [35] – объектом, отвечающим за группировку элементов в GraphicsJS. Элементы должны быть сгруппированы, чтобы к ним было удобно применять одинаковые изменения, например трансформации. Слои можно модифицировать в режиме приостановки (о нем расскажу чуть ниже): это улучшает производительность и впечатления от использования.

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

// создаем надпись для отображения счетчика
var counterLabel = stage.text(10,10, "Swiped: 0", {fontSize: 20});

// слой для листьев
var gameLayer = stage.layer().zIndex(counterLabel.zIndex()-1);

Теперь, сколько бы мы ни создавали листьев в этом слое, они никогда не окажутся поверх текста.

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

Далее добавим функцию для отрисовки листьев, воспользовавшись удобным API для трансформаций, который позволяет перемещать, масштабировать, поворачивать и обрезать как элементы, так и группы элементов. В связке с другими возможностями, которые предоставляет GraphicsJS, – слоями и виртуальным DOM – это очень мощный инструмент.

function drawLeaf(x, y) {
  // выбираем произвольный цвет из палитры
  var index = Math.floor(Math.random() * 5);
  var fill = palette_fill[index];
  var stroke = palette_stroke[index];

  // генерируем произвольные масштабирующий коэффициент и угол поворота
  var scale = Math.round(Math.random() * 30) / 10 + 1;
  var angle = Math.round(Math.random() * 360 * 100) / 100;

  // создаем новый путь (лист)
  var path = acgraph.path();

  // задаем раскраску и рисуем лист
  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);

  // применяем произвольные трансформации
  path.scale(scale, scale, x, y).rotate(angle, x, y);

  return path; 
};

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

Работа с событиями

Все объекты, рабочие области и слои в GraphicsJS умеют обрабатывать события [36]. Список всех доступных событий есть в EventType API [37]. При этом рабочие области поддерживают четыре специальных события [38] для контроля рендеринга.

В примере с игрой мы используем слушатели событий, привязанные к каждому объекту (листу), чтобы заставить листья исчезать по одному при наведении на них мыши. Чтобы добиться этого, добавим следующий код в конец функции drawLeaf, перед оператором return:

path.listen("mouseover", function(){
  path.remove();
  counterLabel.text("Swiped: " + leavesCounter++);
  if (gameLayer.numChildren() < 200) shakeTree(300); 
});

Отсюда также ясно, что для подсчета листьев используется слой.

if (gameLayer.numChildren() < 200) shakeTree(300); 

Заметьте, на самом деле мы не храним здесь количество листьев. Листья – это линии, которые добавляются в тот или иной слой или удаляются оттуда, и потому можно отследить, сколько у нас дочерних объектов (а значит, и сколько листьев остается).

Библиотека GraphicsJS дает возможность использовать виртуальный DOM [39], абстракцию HTML DOM, легкую и независимую от специфики применения SVG/VML в разных браузерах. Эта технология пригодится для реализации целого ряда полезных функций, таких как контроль за всеми объектами и слоями, применение трансформаций к группам и оптимизация рендеринга с помощью методов, которые позволяют отслеживать и контролировать весь его процесс.

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

Благодаря виртуальному DOM, а также обработчикам событий пользователи GraphicsJS могут контролировать рендеринг. О том, как эти вещи связаны, можно прочитать в статье "Производительность [40]" из документации библиотеки.

На время, когда в нашей игре генерируются листья, рендеринг нужно приостановить, а затем возобновить его, как только все изменения будут произведены:

function shakeTree(n){
  stage.suspend(); // приостанавливаем рендеринг
  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)); // добавляем лист
  }

  stage.resume(); // возобновляем рендеринг
}

При таком способе добавления элементов новые листья появляются практически мгновенно.

// первый раз сбрасываем все листья
shakeTree(500);

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

Конечный результат

Заключение

Переход на HTML5 изменил Веб. Когда дело касается современных веб-приложений или даже простого сайта, мы часто сталкиваемся с задачами, которые требуют манипуляций с графикой. Хотя невозможно найти решение, которое идеально работает в абсолютно любой ситуации, я предложил бы вам обратить внимание на библиотеку GraphicsJS. У нее открытый код и open-source лицензия, она очень функциональна и производительна, а также оснащена отличной браузерной поддержкой и множеством фич. Все это делает GraphicsJS интересным, удобным и, конечно, эффективным решением.

Буду рад получить обратную связь по поводу GraphicsJS в комментариях. Уже используете эту библиотеку? Готовы ли рассмотреть возможность ее применения в новом проекте? Интересно было бы узнать, почему, равно как и почему нет. Кстати, сейчас я работаю над созданием списка лучших графических JavaScript-библиотек и статьей, где они будут сравниваться, – так что предлагаю написать в комментариях, какие библиотеки вы бы хотели там видеть.

Cсылки

Автор оригинальной статьи на SitePoint [1]: Роман Любушкин (Roman Lubushkin).

Автор: prentopros

Источник [44]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/258274

Ссылки в тексте:

[1] Introducing GraphicsJS, a Powerful Lightweight Graphics Library: https://www.sitepoint.com/introducing-graphicsjs-a-powerful-lightweight-graphics-library/

[2] хорошо известны: https://www.sitepoint.com/canvas-vs-svg-choosing-the-right-tool-for-the-job/

[3] GraphicsJS: http://www.graphicsjs.org/

[4] VML: https://en.wikipedia.org/wiki/Vector_Markup_Language

[5] Raphaël: https://dmitrybaranovskiy.github.io/raphael/

[6] Snap.svg: http://snapsvg.io/

[7] BonsaiJS: https://bonsaijs.org/

[8] был открыт только прошлой осенью: https://www.anychart.com/blog/2016/09/15/anychart-open-source-powerful-draw-anything-js-graphics-library-graphicsjs/

[9] AnyChart: https://www.anychart.com/

[10] на GitHub: https://github.com/anychart/graphicsjs

[11] пылающий костер: https://playground.anychart.com/gallery/latest/Graphics/Bonfire-plain

[12] вращающаяся галактика: https://playground.anychart.com/gallery/latest/Graphics/Galaxy-plain

[13] дождь: https://playground.anychart.com/gallery/latest/Graphics/Rain-plain

[14] процедурно генерируемые листья: https://playground.anychart.com/gallery/latest/Graphics/Tree-plain

[15] головоломка «Пятнашки»: https://playground.anychart.com/gallery/7.12.0/Graphics/Puzzle_15-plain

[16] документации: https://docs.anychart.com/latest/Graphics/Overview

[17] описании API: https://api.anychart.com/latest/anychart.graphics

[18] прямоугольник: https://api.anychart.com/latest/anychart.graphics#rect

[19] круг: https://api.anychart.com/latest/anychart.graphics#circle

[20] фигуру: https://docs.anychart.com/latest/Graphics/Shapes

[21] пример на CodePen: https://codepen.io/SitePoint/pen/oBPEzE

[22] Даров Смерти: https://harrypotter.wikia.com/wiki/Deathly_Hallows

[23] настройки заливки: https://docs.anychart.com/latest/Graphics/Fill_Settings

[24] контура: https://docs.anychart.com/latest/Graphics/Stroke_Settings

[25] заливки штриховкой и узором: https://docs.anychart.com/latest/Graphics/Hatch_Fill_Settings

[26] вариантов заполнения: https://api.anychart.com/latest/anychart.graphics.vector.HatchFill.HatchFillType

[27] наивного искусства: https://en.wikipedia.org/wiki/Na%C3%AFve_art

[28] ар брют: https://en.wikipedia.org/wiki/Outsider_art

[29] на CodePen: https://codepen.io/SitePoint/pen/VPGQKx

[30] вот так: https://codepen.io/SitePoint/pen/QdVQqb

[31] текстового объекта: https://api.anychart.com/latest/anychart.graphics.vector.Text

[32] паттерн: https://api.anychart.com/latest/anychart.graphics.vector.Stage#pattern

[33] Cookie Clicker: http://orteil.dashnet.org/cookieclicker/

[34] Финальный вариант игры можно посмотреть на CodePen: https://codepen.io/SitePoint/pen/NdLMgB

[35] слоем: https://docs.anychart.com/latest/Graphics/Layers

[36] обрабатывать события: https://docs.anychart.com/latest/Graphics/Events

[37] EventType API: https://api.anychart.com/latest/anychart.graphics.events.EventType

[38] четыре специальных события: https://api.anychart.com/latest/anychart.graphics.vector.Stage.EventType

[39] виртуальный DOM: https://docs.anychart.com/latest/Graphics/Virtual_DOM

[40] Производительность: https://docs.anychart.com/latest/Graphics/Performance

[41] SVG: https://www.w3.org/wiki/HTML/Elements/svg

[42] Canvas: https://www.w3.org/wiki/Html/Elements/canvas

[43] SVG vs. Canvas: https://en.wikipedia.org/wiki/Canvas_element#Canvas_versus_Scalable_Vector_Graphics_.28SVG.29

[44] Источник: https://habrahabr.ru/post/331272/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox