Набрёл на статью со сравнением трёх JS библиотек для рисования в WEB Paper.js, Processing.js и Raphael.js. Думаю она будет интересна не только мне.
Прежде чем начать рисовать что-либо в браузере, спросите себя о следующих трёх вещах:
- Вам нужна поддержка старых браузеров?
Если ответ да, тогда единственный выбор, это Raphaël. Он поддерживает браузеры вплоть до IE 7 и Firefox 3. У Raphaël есть даже ограниченная поддержка IE 6, несмотря на то, что некоторые базовые для библиотеки технологии не могут быть реализованы в этом браузере. - Вам нужна поддержка Android?
Android не поддерживает SVG, так что вам придется использовать Paper.js или Processing.js. Существуют слухи, что Android 4 будет поддерживать SVG, но большинство современных Android устройств уже не будет его поддерживать. - Ваш рисунок интерактивный?
Raphaël и Paper.js сосредоточены на взаимодействии с рисуемыми элементами посредством клика мышкой, перетаскивания и касания. Processing.js не поддерживает никаких события уровня объекта, потому обработать движения пользователя в нём довольно сложно. Processing.js может рисовать классную анимацию на Вашей домашней страничке, а Raphaël и Paper.js более подходят для интерактивных приложений.
Paper.js, Processing.js и Raphaël на данный момент являются лидирующими библиотеками для рисования в Web. Также есть пара новичков, чья популярность растёт, и конечно Вы всегда можете использовать Flash, но эта троица хорошо работает с HTML5 и имеет широчайшую поддержку среди производителей браузеров.
Выбор наиболее подходящего фреймворка может определить успех Вашего проекта. Эта статья опишет преимущества и недостатки каждой библиотеки и даст достаточно информации чтобы сделать верный выбор.
Весь код приведённый в статье является Open Source и доступен на демонстрационной странице созданной специально для поддержки этой статьи.
Общие характеристики
Paper.js | Processing.js | Raphaël | |
---|---|---|---|
Технология | Тэг canvas | Тэг canvas | SVG |
Язык | PaperScript | Processing script | JavaScript |
Браузеры с | IE 9 | IE 9 | IE 7 |
Мобильные браузеры | Да | Да | Только iOS |
Модель | Векторная и растровая | Растровая | Векторная |
Размер файла ядра | 56 KB | 64 KB | 20 KB |
Все используют исключительно JavaScript, но каждый из фреймворков реализуют свой подход. Raphaël написан непосредственно на JavaScript, зато Paper.js использует PaperScript, а Processing.js использует свой собственный язык. Все они поддерживаются в Firefox, Chrome и Safari, проблема только с Internet Explorer — Paper.js и Processing.js используют тэг canvas и поэтому требуют минимум IE 9.
PaperScript это расширение языка JavaScript позволяющие писать код не загрязняющий глобальное пространство имён. Это уменьшает вероятность конфликтов в JavaScript. PaperScript также напрямую поддерживает математические примитивы, такие как Point и Size: Вы можете складывать две точки вместе, так, как-будто это простые числа.
Processing.js основан на фреймворке под названием Processing, который использует для работы Виртуальную Java машину. Вы определяете int и float вместо var, а также можете использовать наследование в Java-стиле. Не смотря на то, что скрипт на Processing.js более походит на Java, это всё же JavaScript и не требует более специфических для Java вещей.
Использование любой из трёх библиотек не составит проблем, если Вы знакомы с JavaScript.
Начнём
Для начала импортируем каждую библиотеку. В каждом случае этот процесс несколько различается
Подключаем Paper.js
Paper.js определяет тип скрипта как text/paperscript и ID целевого элемента canvas на котором мы будем рисовать.
<head>
<script src="paper.js" type="text/javascript" charset="utf-8"></script>
<script type="text/paperscript" canvas="paperCircle" src="paper_circle.pjs" id="script"></script>
</head>
<body>
<canvas id="paperCircle" class="canvas" width="200" height="200" style="background-color: white;"></canvas>
</body>
Подключаем Processing.js
Processing.js использует data-processing-sources атрибут тега canvas для инициализации. Автор использовал .java расширение для скрипта с файлом Processing, чтобы его редактор кода правильно отобразил подсветку синтаксиса. Другие авторы могут использовать расширения .pde или .pjs. Как Вам удобнее.
<head>
<script src="processing.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<canvas width="200" height="200" class="canvas" data-processing-sources="processing_circle.java"></canvas>
</body>
Подключаем Raphaël
Raphaël подключается как и любой другой JavaScript файл. Он отлично работает совместно с jQuery’s ready функцией и со всеми остальными JS фреймворками.
<head>
<script src="raphael-min.js" type="text/javascript" charset="utf-8"></script>
<script src="raphael_circle.js" type="text/javascript" charset="utf-8"></script>
</head>
Теперь мы готовы рисовать
Объектно-Ориентированное рисование
Оба Paper.js и Raphaël используют объектно-ориентированный подход к процессу рисования: Вы рисуете круг и получаете объект Круг. Processing.js просто рисует круг и ничего не возвращает. Следующий код иллюстрирует как это происходит. Давайте начнём с кружка размерами 100 на 100 по центру экрана.
Paper.js:
var circle = new Path.Circle(new Point(100, 100), 10);
circle.fillColor = '#ee2a33';
Raphaël:
var paper = Raphael('raphaelCircle', 200, 200);
var c = paper.ellipse(100, 100, 10, 10);
c.attr({'fill': '#00aeef', 'stroke': '#00aeef'});
Processing.js:
void setup() {
size(200, 200);
}
void draw() {
background(#ffffff);
translate(100, 100);
fill(#52b755);
noStroke();
ellipse(0, 0, 20, 20);
}
Каждый кусок кода рисует один и тот же круг. Разница в том, что Вы можете потом с ним сделать.
Paper.js создаёт круг как path объект. Мы можем сохранить его и изменить в дальнейшем. В Paper.js, circle.fillColor = 'red'; рисует красным цветом круг, а circle.scale(2) делает его в два раза больше.
Raphaël как и Paper.js’ реализует объектно-ориентированную модель. В Raphaël, мы можем изменить цвет нашего круга используя circle.attr('fill', 'red'); и изменить его размеры написав circle.scale(2, 2);. Главная идея состоит в том, что круг, это объект к свойствам которого мы можем получить доступ позднее.
Processing.js не использует объектов; функция ellipse() не возвращает значения. Как только мы нарисовали круг используя Processing.js, он становится частью создаваемого изображения, как при обычном рисовании чернилами по бумаге; это ни какой-то отдельный объект, которому можно изменить свойства. Для изменения цвета мы должны нарисовать такой же круг, но другого цвета поверх первого.
Когда мы вызываем функцию fill(), изменяется цвет для всех примитивов отображаемых далее. После вызова функции translate() и fill(), все фигуры будут закрашены зелёным.
Так как функции изменяют сразу всё, мы легко можем получить совсем неожиданные эффекты. Вызываешь безвредную функцию и вдруг всё становится зелёным! Processing.js предоставляет функции pushMatrix() и popMatrix() для изоляции изменений, но их ещё надо не забыть вызвать.
Отсутствие объектов у Processing.js означает то, что сложные рисунки обрабатываются значительно быстрее. Paper.js и Raphaël хранят ссылки на все нарисованные объекты, что увеличивает количествотребуемой памяти и на сложной графике заметно замедляют приложение. Processing.js не содержит ссылок на создаваемые объекты, в итоге каждая часть рисунка занимает очень мало памяти. Объектный подход оправдан в случае, если Вы хотите получить доступ к объекту позже, в противном случае это напрасная трата ресурсов. Paper.js даёт возможность избежать ненужного расхода памяти там, где это не нужно, для этого используется Symbol который растеризует объект, но конечно нужно всё спланировать заранее чтобы приложение работало достаточно быстро.
Различные подходы определяют весь стиль работы с библиотеками. Конечно это влияет и на то, как в них работать с анимацией.
Заставим их двигаться
Вращение кругов не слишком зрелищно, поэтому давайте пусть вокруг круга вертится квадрат.
Анимация в Processing.js
Processing.js поддерживает анимацию используя функции setup() и draw() функции, примерно так:
float angle = 0.0;
void setup() {
size(200, 200);
frameRate(30);
}
void draw() {
background(#ffffff);
translate(100, 100);
fill(#52b755);
noStroke();
ellipse(0, 0, 20, 20);
rotate(angle);
angle += 0.1;
noFill();
stroke(#52b755);
strokeWeight(2);
rect(-40, -40, 80, 80);
}
Setup функция вызывается в момент старта приложения. Мы говорим Processing.js с частотой 30 кадров в секунду, в итоге наша функция draw() будет вызываться 30 раз в секунду. Это число может показаться высоким, но это нормально если мы хотим получить плавную анимацию.
Функция draw() вначале заливает весь холст одним цветом; это закрасит всё, что осталось от предыдущего кадра. Это главная особенность Processing.js: мы не манипулируем объектами, поэтому мы должны очистить всё, что осталось с предыдущего кадра.
Далее, мы переводим фокус в точку 100,100. Это позиционирует отрисовку на 100 пикселей слева и 100 пикселей от верха холста для всех операций рисования, пока мы не изменим координаты. Далее мы изменяем предыдущее значение угла наклона. Это значение увеличивается с каждым кадром заставляя квадрат вращаться. И последний шаг, это отображение квадрата с использованием функций fill и rect.
Обычно функция rotate() Processing.js оперирует радианами вместо градусов. Поэтому мы каждый раз увеличиваем значение угла на 0.2, а не большее значение, к примеру 3. Это один из многих случаев при программном рисовании когда нам нужна тригонометрия.
Анимация Paper.js
В Paper.js простую анимация реализовать проще, чем в Processing.js, используя постоянный объект rectangle:
var r;
function init() {
var c = new Path.Circle(new Point(100, 100), 10);
c.fillColor = '#ee2a33';
var point = new Point(60, 60);
var size = new Size(80, 80);
var rectangle = new Rectangle(point, size);
r = new Path.Rectangle(rectangle);
r.strokeColor = '#ee2a33';
r.strokeWidth = 2;
}
function onFrame(event) {
r.rotate(3);
}
init();
Мы используем состояние нашего квадрата в виде объекта, и Paper.js управляет рисованием на экране. С каждым кадром мы вращаем его понемногу. Paper.js управляет всеми трансформациями, поэтому мы не должны перерисовывать всё в ручную в начале каждого кадра или следить за текущим значением угла поворота или беспокоится о том, чтобы не задеть другие объекты.
Анимация Raphaël
Анимация на Raphaël написана на стандартном JavaScript, т.е. Raphaël не определяет каких-то специальных функций для управления кадрами. Вместо этого мы используем обычную функцию setInterval().
var paper = Raphael('raphaelAnimation', 200, 200);
var c = paper.ellipse(100, 100, 10, 10);
c.attr({
'fill': '#00aeef',
'stroke': '#00aeef'
});
var r = paper.rect(60, 60, 80, 80);
r.attr({
'stroke-width': 2,
'stroke': '#00aeef'
});
setInterval(function() {
r.rotate(6);
}, 33);
Raphaël похож на Paper.js в своём объектно-ориентированном подходе. У нас есть квадрат, и мы вызываем его метод rotate(). Таким образом мы можем вращать квадрат используя всего несколько строк кода.
Взаимодействие
Raphaël раскрывает свои преимущества когда появляется нужда в добавлении интерактивности в рисунок. Он предоставляет событийную модель, сходную с обычной для JavaScript, позволяя легко обнаруживать клики мышкой, начало перетаскивания или прикосновения пользователя. Давайте добавим квадрату реакцию на клик мышкой.
Взаимодействие в Raphaël
var paper = Raphael('raphaelInteraction', 200, 200);
var r = paper.rect(60, 60, 80, 80);
r.attr({'fill': '#00aeef', 'stroke': '#00aeef'});
var clicked = false;
r.click(function() {
if (clicked) {
r.attr({'fill': '#00aeef', 'stroke': '#00aeef'});
} else {
r.attr({'fill': '#f00ff0', 'stroke': '#f00ff0'});
}
clicked = !clicked;
});
Функция click() в Raphaël работает как в jQuery, и её можно навесить на любой объект. Получив событие клика, изменить цвет квадрата не составляет проблемы. Raphaël имеет дополнительные функции для поддержки перетаскивания, прохождения курсора над объектом и все другие события которые доступны в JavaScript.
Взаимодействие в Paper.js
Paper.js идёт другим путём для обслуживания задач взаимодействия, но и он довольно прост:
var hitOptions = {
fill: true,
tolerance: 5
};
function init() {
var point = new Point(60, 60);
var size = new Size(80, 80);
var rectangle = new Rectangle(point, size);
r = new Path.Rectangle(rectangle);
r.fillColor = '#ee2a33';
}
function onMouseUp(event) {
var hitResult = project.hitTest(event.point, hitOptions);
if (hitResult && hitResult.item) {
if (hitResult.item.clicked) {
hitResult.item.fillColor = '#ee2a33';
} else {
hitResult.item.fillColor = '#f00ff0';
}
hitResult.item.clicked = !hitResult.item.clicked;
}
}
init();
Paper.js работает с курсором используя концепцию названную “hit testing” Точка нажатия высчитывается по текущим координатам курсора и библиотека получает расположенный там объект. Настройки позволяют конфигурировать поведение программы: можно указать такие вещи как: на сколько близко курсор должен быть, чтобы засчитать объекту клик, является-ли объектом всё его внутренне пространство или только край. Мы можем применить это обнаружение кликов к любому объекту (или группе объектов) в Paper.js.
Команда Paper.js также добавила объектный подход, сходный с Raphaël всего несколько недель назад (на февраль 2012). События должны появиться в следующей версии.
Взаимодействие в Processing.js
У Processing.js обнаружение клика мышки достаточно запутано. Объектный стиль не поддерживается, так-что мы должны рассчитывать в основном на свои собственные силы.
float bx;
float by;
int bs = 20;
boolean bover = false;
boolean clicked = false;
void setup() {
size(200, 200);
bx = width/2.0;
by = height/2.0;
noStroke();
fill(#52b755);
frameRate(10);
}
void draw() {
background(#ffffff);
// Test if the cursor is over the box
if (mouseX > bx-bs && mouseX < bx+bs && mouseY > by-bs && mouseY < by+bs) {
bover = true;
} else {
bover = false;
}
translate(100, 100);
rect(-40, -40, 80, 80);
}
void mousePressed() {
if (bover) {
if (clicked) {
fill(#52b755);
} else {
fill(#f00ff0);
}
clicked = !clicked;
}
}
Как только Processing.js отрисовывает квадрат, он забывает о нём. Мы хотим, чтобы при клике на квадрат он менял свой цвет, но скрипт этого не знает, поэтому мы должны делать все вычисления сами. Функция draw() определяет позицию курсора и высчитывает лежит-ли он в пределах нашего квадрата.
Для одного квадрата код не такой уж и страшный, но к примеру для круга нужно будет считать каждый раз Пr2. А более сложные фигуры, такие как овалы, кривые и сложные формы потребует еще больше математики.
Нет явного победителя
Каждый фреймворк имеет свои преимущества. Каждая библиотека предоставляет возможности для создания классных демок и ещё более классных приложений.
Преимущества Paper.js
Paper.js отлично подходит для манипулирования сложными фигурами. Он может вращать, скручивать и трансформировать любой объект сотней различных методов. Это даёт возможность модифицировать объекты используя жесты мышкой (gestures). Новый Google Music Tour, заставляет цветные линии двигаться под музыку, показывая как эта библиотека справляется со сложными изменениями простых фигур.
Ещё один вау-фактор в Paper.js это поддержка растровой графики . Paper.js может полностью изменить способы, которыми отображаются изображения — к примеру превращая их в спирали или кубиковые полотна (Q*bert boards).
Преимущества Processing.js
Основное преимущество Processing.js’ это его скорость, которая позволяет создавать сложную анимацию даже на слабых машинах. Тут есть много примеров, также отличный пример плавности анимации на Processing.js используется на сайте Ricardo Sánchez.
Рассекающие воду хвосты и плавающие тела рыб выглядят очень натурально. Processing.js позволяет добиться этого просто, используя кривые и настраиваемые анимации.
Processing.js также поддерживает сложные эффекты на элементах, такие как затенение, освещение и 3-D трансформации. Если Вы хотите быстро создать сложные анимации на канве, то Processing.js является лучшим выбором.
Преимущества Raphaël
Одной из лучших возможностей Raphaël является поддержка Internet Explorer 7 и 8. Если Ваше приложение должно поддерживать старые браузеры, то Raphaël является единственным выбором.
Ещё одним важным преимуществом Raphaël является его общество. Raphaël старше чем Paper.js и Processing.js и поэтому у него было больше времени на создание галереи примеров, туториала и описания типичных проблем и способов их решения. У него есть встроенная поддержка easing, анимации трансформаций и обработчиков событий, которую мы видели в интерактивном примере; также у него есть собственная библиотека для создания графиков.
Также у Raphaël отличный набор разнообразных инструментов.
Инструменты
Если Вы работали с Flash, отсутствие инструментов для этих библиотек Вас расстроит. Многие фреймворки позволяют редактировать SVG изображения, но ни один из них не предоставляет drag-and-drop способа для создания приложений.
Тут приведены некоторые простые инструменты, но они скорее могут быть рассмотрены в качестве доказательства самой возможности создавать такие программы, нежели самостоятельные приложения. Adobe работает над программой названной Edge, но ей ещё много предстоит пройти чтобы стать нормальным инструментом.
Если Вы хотите, drag and drop, то Web анимация ещё не для Вас. На данный момент создание анимации в этой области более похоже на программирование видео игр. Написание кода для отображения круга сложнее, чем просто кликнуть и перетащить фигуру из палитры, на это даёт много преимуществ при создании более сложных приложений.
Давайте создадим что-нибудь более сложное
Итак, мы рассмотрели несколько простых примеров, познакомились в преимуществами и недостатками каждой платформы и поняли, в каком случае какая из них подходит лучше. Каждая библиотека имеет свои плюсы и минусы, но всё-таки основываясь только на примитивных примерах о них довольно сложно судить.
Для сравнения каждой библиотеки были нарисованы несколько шестерёнок. Каждая из которых состоит из двух кругов, с набором зубцов вокруг внешнего круга.
Когда все поверхности залиты одним цветом, фигура похожа на шестерёнку.
Шестерёнка будет немного поворачиваться на каждом кадре анимации. Основная шестерёнка будет задавать скорость, а все остальные будут вращаться зависимо от неё. Шестерёнки будут располагаться, сцепляться и вращаться друг о друга основываясь на сумасшедшем количестве тригонометрии. Расположите их на одном холсте и Вы получите сложную систему шестерёнок.
Paper.js:
Processing.js:
Raphaël:
Ну, это был не совсем Raphaël. Функция вращения работает в Raphaël не так как в Paper.js и Processing.js. Raphaël не поддерживает вращение вокруг фиксированной точки. Вместо этого зубцы шестерёнки каждый раз перерисовываются независимо и летят вокруг по воздуху вместо того, чтобы вращаться вокруг центра. Единственное как автор может представить себе это можно сделать, это отрисовать шестерёнку целиком и вращать её, но там слишком много математики чем он хотел-бы приводить. Если кто-то хочет попробовать реализовать это сам, добро пожаловать, это Open Source.
Будущее рисования в Web
Мы играемся с каждой новой освоенной технологией: мы надеемся, что она решит многие наши проблемы и это окупит инвестиции в её изучение. Технологии набирают и теряют популярность, но в этом участвуют многие факторы, к примеру поддержка производителей или требования бизнеса. Будущее нашей индустрии это частенько игра в догадки.
Сегодня Flash выглядит не лучшей технологией для изучения. У Flash есть классные инструменты разработки, годы накопленного опыта разработки и большое общество, но даже Adobe уходит от него.
С SVG похожая ситуация. Браузеры его поддерживают, но не уделяют слишком много внимания.
Каждый производитель браузеров постоянно работает над увеличением скорости отрисовки canvas, для получения возможности использования аппаратной акселерации и лучшей поддержки таких библиотек как Paper.js и Processing.js. Все мобильные браузеры А-класса поддерживают canvas, и их разработчики постоянно работают над улучшением качества этой поддержки.
Автор: Pavel_Osipov