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

Перевод статьи. Англоязычный оригинал опубликован на 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 строк кода сделать простую арт-игру в жанре таймкиллера.
Библиотек, облегчающих работу с 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 нужно подключить саму библиотеку и создать блочный элемент 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] в рабочей области и, наконец, вставить текст в паттерн.
В следующей части статьи я покажу, как с помощью GraphicsJS нарисовать игру-кликер типа Cookie Clicker [33] меньше чем за 50 строк кода.
Название этой игры – «Дворник на ветру». В ней пользователь выступает в роли дворника, который подметает улицу ветреным осенним днем. Здесь частично используется код примера с процедурно генерируемыми листьями [14] из галереи GraphicsJS.
Финальный вариант игры можно посмотреть на CodePen [34] (или в конце этой статьи).
Начнем с создания рабочей области (как в предыдущем примере). И затем объявим несколько исходных переменных:
// создаем рабочую область
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-библиотек и статьей, где они будут сравниваться, – так что предлагаю написать в комментариях, какие библиотеки вы бы хотели там видеть.
Автор оригинальной статьи на 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
Нажмите здесь для печати.