Pull to refresh

Comments 53

Спасибо за замечательную либу. Попробую как будет возможность
Чем она лучше/хуже существующих библиотек? Было бы очень интересно увидеть сравнение производительности.

Не заметил что это перевод, извиняюсь — пошел смотреть на офф.сайт.
Хороший вопрос. Я как раз для этого сделал вот такую табличку сравнения популярных canvas библиотек — docs.google.com/spreadsheet/ccc?key=0Aqj_mVmuz3Y8dHNhUVFDYlRaaXlyX0xYSTVnalV5ZlE#gid=0

Мы немного уступаем по документации и демкам Kinetic, Paper, Easel. Но зато строго следим за качеством кода, модулярностью, простотой, и конечно же тестами. А ещё широкая поддержка браузеров из-за того, что используем базовые фичи и избегаем вынюхивания браузеров (browser sniffing).
Есть ли что-то подоное paperjs.org/examples/path-simplification/? И есть ли возможность у любого объекта получить данные как-он нарисован, что б сохранить/загрузить его?
Да, есть поддержка свободного рисования. Если откроете fabricjs.com/kitchensink/ и нажмёте «Enter drawing mode» внизу справа, можно начинать рисовать — цвет и диаметр кисти меняется. После завершения, кривые автоматически конвертируются в Path объекты, с которыми можно делать всё тоже что и с другими объектами. В будущем планирую добавить дополнительные кисти, и наверное так чтобы легко можно было свои добавлять.

Сохранение/загрузка — это вообще отдельная тема :) У нас довольно серьёзный фокус именно на это; можно сохранять в разные форматы — SVG, object, JSON, data URL. И соответственно подгружать обратно. Об этом — в следующих частях.

Все объекты имеют «type» аттрибут: «rect», «circle», «path», «path-group», и т.д.

А если нужна именно информация о кривых, то конечно её легко можно вытащить из одного из аттрибутов объекта.
path-simplification — это не просто свободное рисование, это алгоритм упрощения пути (удаления лишних точек). В paperjs он в среднем удаляет 80% точек с пути, что сильно снижает объём хранимых данных. В fabric я этого алгоритма не нашёл так что видимо его нужно реализовывать самостоятельно.
Да, в Fabric такого алгоритма нет. Хотя интересная оптимизация; возможно добавим в будущем.
на родной язык грех не перевести :)
Сейчас выбираем между Kinetic JS и вашей библиотекой. Fabric явно пошустрее будет, на мой взгляд.
Только несколько смущает позиционирование объектов: вот пример
Я так понимаю, отсчет координат идет о центра объекта? Если да, то с какой целью так сделано и можно ли это поведение изменить?
Как раз только что закончил работу над тем чтобы можно было указать точку трансформации. До этого она всегда была в центре объекта. Вот подключил branch с гитхаба, посмотрите как работает — jsfiddle.net/PNb8Y/27/
То, что надо! Спасибо
А раз нужна объектная модель и редактирование, то чем плох собственно svg?
Если SVG элементов много они сильно тормозят. SVG-нода почти DOM-нода.
«Ручная» перерисовка большого количества объектов на канвасе тормозить не будет?
Есть где-нибудь сравнительные тесты?
Есть секция бенчмарков — fabricjs.com/benchmarks/

Вот посмотрите как мы рендерим знаменитую фигуру тигра состоящую из ~2500 кривых — fabricjs.com/raphael-vs-fabric-complex/

Когда таскаешь по холсту, всё это дело перерисовывается на каждое движение мыши. На десктопных современных браузерах даже такая сложная фигура «работает» довольно быстро. Сам движок я стараюсь писать как можно эффективней; производительность штука важная :)

Есть ещё идеи по некоторым оптимизациям на будущее.
fabricjs.com/raphael-vs-fabric-simple/?100
Здесь канвас заметно подтормаживает, кружочек за мышкой не успевает. SVG же не тормозит вовсе.

fabricjs.com/raphael-vs-fabric-simple/?5000
Здесь SVG начинает подтормаживать, но не так сильно, как канвас в предыдущем примере. Но на канвасе кружочки здесь не двигаются вовсе. Точнее через пару секунд передвигаются.

Браузер: Firefox 17.0.1
Да, зависит от браузера. SVG в последнее время становится всё быстрее и быстрее. Но у меня в Chrome 25, например, 1000 кружков работают с одинаковой скоростью как на канвасе так и в SVG. Только с 5000 канвас начинает подтармаживать.

Заметьте что в изначальной (статичной) отрисовке канвас обычно всегда выигрывает у SVG.
Заметьте что в изначальной (статичной) отрисовке канвас обычно всегда выигрывает у SVG.

Видимо, тоже от браузера зависит. У меня с 5000 объектов
Raphael: 897 ms
fabric: 1373 ms
Когда начинал работать над Fabric 3 года назад, производительность SVG оставляла желать лучшего. Особенно когда имеешь дело со сложными фигурами, их анимацией, и т.д. В SVG всё-таки каждый элемент — это node в документе. DOM всегда был медленным. Сейчас ситуация получше но не намного.

А в Fabric фигуры — это просто Javascript объекты в памяти. Очень лёгкие, не сравнить с элементами. К тому же объекты используют prototypal inheritance; большинство методов вообще только на классах-потомках. Это делает их супер лёгкими — только, по сути дела, набор простых параметров как цвет, ширина, высота, координаты, и т.д.
Как-то давно пользовался jcscript.com/. К сожалению не в курсе как развивался проект с тех пор.
Если кого интересуют все доступные альтернативы, то они есть:
jster.net/category/canvas-wrappers

А Fabric.Js одна из самых популярных либ для канваса.
Люди сведущие, скажите: отлавливаются ли события мыши, когда нужный объект перекрывается другим с областью прозрачности?
Посмотрел демки Fabric, там достаточно ткунуть в прозрачный ограничивающий прямоугольник верхнего объекта, чтобы она захватил на себя событие мыши. Хотя визуально событие возникло над вторым объектом. Что делать?
спасибо за работу, очень круто и интуитивно!
А есть ли возможность смены z-index, т.е. перемещать объекты выше/ниже других объектов?
Спасибо!

Да, конечно. Есть 4 метода для «перемещения» объектов по z-index — bringForward(), sendBackwards(), bringToFront(), sendToBack().

А вообще canvas.getObjects() массив собственно и является стэком видимости объектов. То есть первый добавленный объект будет перекрываться вторым, второй — третьим, и т.д. Так что можно даже вручную поменять порядок как надо.
отличненько, я просто здесь fabricjs.com/kitchensink поигрался с объектами и искал возможность переместить объект наверх, но не нашел
Ну во-первых, внутри тега source другие теги не работают, поправьте пожалуйста.

Во-вторых, вы так говорите, как будто Fabric.js очень уникален. На самом деле canvas-библиотек с объектной моделью куча. Да хотя бы LibCanvas, JCScript, PaperJS. Первые две кстати от хабраюзеров TheShock и asavin. Что касается уникальности — уникален Fabric своей интеграцией с SVG и интерактивом, хотя конечно насчёт последнего можно поспорить. У PaperJS интерактив вполне можно реализовать: да хотя бы paperjs.org/examples/path-simplification/. LibCanvas тоже: libcanvas.github.com/ui/projective-image.html.

Ну во-первых, внутри тега source другие теги не работают, поправьте пожалуйста.


Ага, не заметил. Исправлю.

Насчёт уникальности — я в принципе на неё не претендую :) Хотя не забывайте что начинал я это всё почти 4 года назад, когда ни Easel.js, ни Kinetic.js, и тем более ни Paper.js ещё не было даже в задумке.

Помню в то время был Cake.js (https://code.google.com/p/cakejs/source/detail?r=1), который сейчас уже умер.

Но это не важно. Важно качество. Именно на этом у нас фокус. А также, такие вещи как поддержка большого количества сред (cross-browser), тесты, высокая производительность, чистый код, и следование стандартам (как разработчик Prototype.js я с этим хорошо знаком). К сожелению всё ещё до сих пор сложно найти библиотеку сочетающие в себе _все_ эти качества. То одно, то другое, да и пропущено.

Даже взять, к примеру, упомянытые вами LibCanvas и JCScript (о которых я до этого не слышал). Посмотрел, ради интереса. Добавил в таблицу сравнения — docs.google.com/spreadsheet/ccc?key=0Aqj_mVmuz3Y8dHNhUVFDYlRaaXlyX0xYSTVnalV5ZlE#gid=0

JCScript выглядит интересно. Видно влияние jQuery. Документация хорошая, но тесты полностью отсутствуют. Не модулярный, хотя с 58КБ (minified) это не страшно. SVG парсера нет, и интерактивный слой самый базовый (таскать можно а вращать/масштабировать нет). Перетаскивание у меня в Chrome кстати как-то медленно. В коде вижу что методы присваеваются напрямую объектам во время создания (Зачем? Это же абсолютно не эффективно. Возможно из за этого?).

LibCanvas выглядит действительно впечатляюще. Ну и размер впечатляющий (106KB плюс AtomJS как dependency). Много функционала, похоже что в основном ориентировано на игры. Опять же, тестов нет. Документация минимальная. Модулярность вроде как отсутствует. И к тому же ещё использует ES5 аксессоры, что полностью исключает IE<9 и другие пожилые браузеры. Смотря на код, вижу ещё и навешивание методов на хост-объекты. Проблемы с этим мы уже проходили в Prototype.js — я даже пост на эту тему когда-то писал :) perfectionkills.com/whats-wrong-with-extending-the-dom/ Зато в LibCanvas интерактив хороший; действительно почти как в Fabric.
> Хотя не забывайте что начинал я это всё почти 4 года назад, когда ни Easel.js, ни Kinetic.js, и тем более ни Paper.js ещё не было даже в задумке.
Ну тогда да, вы имеете право на неё претендовать.

> Важно качество
Согласен полностью.

Что касается JCScript, он выглядит интересно, но довольно-таки сыроват… и не очень, в общем. Что касается SVG-парсера — это хорошо, но всё-таки зачем он так нужен?..

LibCanvas — да, кстати сам TheShock, если не ошибаюсь, работает в области игр. ES5-аксессоры — это не баг, а фича :). Ведь в IE<9 canvas-а и так нету, а юзать разные excanvas — те ещё костыли. Если уж так, можно взять dojox.gfx, который для рисования юзает кроме canvas ещё и vml, svg и silverlight :).

Ну в общем да, Fabric тоже выглядит здорово.

P.S. Кстати я тоже занимаюсь своим фреймворком для canvas :).
LibCanvas — да, кстати сам TheShock, если не ошибаюсь, работает в области игр.

Не ошибаетесь) Работаю в Wargaming.net, а если быть точным, то в киевском подразделении — Persha студия

P.S. Кстати я тоже занимаюсь своим фреймворком для canvas :).

как прогресс, кстати?)
Делаю :). Часто пишу г-код и, не зная как с ним справиться, выкидываю нафиг и переписываю. Из-за этого часто задерживаюсь(.
Сейчас текст малость доделаю, скину)
Сначала обращу внимание на ошибки.

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

Так что дату «Last updated» можно смело ставить 21.12.2012 ;)

Docs в LibCanvas таки присутствуют, хоть и только на русском. В репозитории папка Docs. Из недостатков — не полная, да, но я в работе) А вот AtomJS полностью покрыт английской документацией и тестами.

Модулярность вроде как отсутствует

Если под модулярностью имеете ввиду подключить только необходимые модули, то она вполне есть в билдере:
shock@shock-notebook:~/dev/libcanvas/Builder$ ./build Point Point3D > result.js
/**
 * LibCanvas
 * 
 * Included Components:
 *   LibCanvas/LibCanvas
 *   LibCanvas/Geometry
 *   LibCanvas/Point
 *   LibCanvas/Point3D
 */


Опять же, тестов нет

Все вещи, мало связанные с отрисовкой я стараюсь выносить в AtomJS. Например, atom.Color или atom.Events, где они уже покрываются тестами. Я к сожалению не могу открыть ваши тесты, где-то в районе 260-ого зависает, но у вас в основном тестируется именно такое — fabric.Color, fabric.Observable, установка свойств и тому подобное, но не отрисовка. Не отрицая полезности unit тестов для LibCanvas я использую подход функционального тестирования. AtomJS, как базовый фреймворк, обязательно покрывается Unit тестами)

Ну и размер впечатляющий (106KB плюс AtomJS как dependency).

Последняя версия 164Kb + 108Kb AtomJS)

И к тому же ещё использует ES5 аксессоры, что полностью исключает IE<9 и другие пожилые браузеры.

Это фича. Я специально отказался от IE<9, т.к. там нереально реализовать всё необходимое для обратной совместимости с Canvas. Например, отрисовку одного Canvas в другой)

Смотря на код, вижу ещё и навешивание методов на хост-объекты.

В новой версии я сделал это опциональным. Всё так же, при желании можно расширить прототипы, но теперь библиотека не требует этого

На счёт nodeJS скажу, что лично я не юзал, но ребята на сервере его юзают)

this.callSuper('initialize', options);

Кстати, я нашёл отличную альтернативу этому)

Зато в LibCanvas интерактив хороший; действительно почти как в Fabric.

Нескромно замечу, что интерактив в LibCanvas намного лучше, чем в Fabric. Особенно в плане оптимизации и разнообразии возможностей) Не зря у LibCanvas в качестве примеров игры, а у "обычных" фреймворков — всякая банальщина вроде изменения векторных фигур.

Ну вот к примеру Various: Static Canvas. Что я вижу? 3 статичных объекта и 1 движущийся. Двигается один, а перерисовывается все 4 из-за того, что вызывается canvas.renderAll(). В LibCanvas мы при изменении дёргаем element.redraw(), а уже фреймворк считает, что надо перерисовать, смотрит, не затрём ли мы что-то другое и так далее. В итоге, на поле может быть сотни объектов, а проц чувствует себя спокойно.

Хитрая работа со слоями и подобная перерисовка вообще позволяют держать десятки тысяч объектов на карте и проц будет чувствовать себя прохладно)

Ну и мне всегда было непонятно, почему во всех либах круто раскрыта всякая банальщина вроде работы с вектором, но никаких интересностей типа графических редакторов и игр) Единственный интересный экземпляр — impactjs.com/, но там исходники закрыты и она платная. Посмотреть бы хоть на какую-то простотень а-ля Пятнашек, чтобы знать, как библиотека показывает себя в реальном проекте.
На самом деле стоило смотреть на новую версию, которую я разрабатываю и как раз перелил в основной бранч. Там и коммиты регулярные и вес побольше.


Отлично, подправил.

Насчёт тестов — да, в unit tests проверяю только логику. Функциональные есть, но не настолько хорошие и достаточные насколько хотелось бы. Вот например когда-то пытался использовать оффициальный SVG test suite — fabricjs.com/test/functional/ Также есть базовая проверка для Node и SVG экспорта. Будем всё это дополнять в будущем. Тестирование прорисовки через unit tests — это вообще отдельная (интересная, но не без проблем) тема.

Это фича. Я специально отказался от IE<9, т.к. там нереально реализовать всё необходимое для обратной совместимости с Canvas.


Это понятно. Но для моих целей не подходит. Даже просто показать картинку человеку и позволить её двигать и вращать по канвасу — уже хорошо. Не обязательно поддерживать полный функционал. Получается что-то вроде «progressive enhancement».

В новой версии я сделал это опциональным. Всё так же, при желании можно расширить прототипы, но теперь библиотека не требует этого


Это хорошо :)

На счёт nodeJS скажу, что лично я не юзал, но ребята на сервере его юзают)


Мы на printio.ru запускаем Fabric прямо на сервере, на Node. Всё просто летает (когда-то давно мучались с Jaxer'ом, если слышали о таком). Канвас сериализуется на клиенте, посылаем JSON на сервер, загружаем через Node и получаем картинку в любом разрешении (обычно здоровенном) через секунду.

Кстати, я нашёл отличную альтернативу этому)


Ну там в принципе и искать не надо :) Все альтернативы уже известны. Можно конечно сократить буквально до `$super()`. Но у всего как плюсы так и минусы.

Ну вот к примеру Various: Static Canvas. Что я вижу? 3 статичных объекта и 1 движущийся. Двигается один, а перерисовывается все 4 из-за того, что вызывается canvas.renderAll(). В LibCanvas мы при изменении дёргаем element.redraw(), а уже фреймворк считает, что надо перерисовать, смотрит, не затрём ли мы что-то другое и так далее. В итоге, на поле может быть сотни объектов, а проц чувствует себя спокойно.


Да, есть такое дело. Хочу заметить что в Fabric `renderAll` тоже проходит по всем объектам и говорит каждому отрисовать себя. Однако оптимизацию типа «dirty rectangles» пока не делали (вот открытый тикет кстати — github.com/kangax/fabric.js/issues/318).

Важно понимать что Fabric не позиционирует себя как framework для создания игр — у нас нет абстракций для работы со спрайтами, звуком, collision detection, и т.д. Поэтому, например, не было особого приоритета оптимизировать тысячи объектов на канвасе. Однако для удобной работы с коллажами Fabric по-моему идеален. Именно для этого я и создал его; и если посмотрите на наш редактор дизайнов — printio.ru/tees/new — я например редко вижу что-то более удобное чем у нас (даже у гигантских западных аналогов — www.zazzle.com/cr/design/pt-zazzle_shirt?style=value_tshirt)

Хитрая работа со слоями и подобная перерисовка вообще позволяют держать десятки тысяч объектов на карте и проц будет чувствовать себя прохладно)


Да конечно. Kinetic.js этим постоянно хвастается :) Возможно добавим поддержку слоёв в будущем. Пока только два статичныx — для всех объектов и для прямоугольника выбора объектов. Добавить поддержку не сложно, но надо продумать как иметь дело с групами.

Ну и мне всегда было непонятно, почему во всех либах круто раскрыта всякая банальщина вроде работы с вектором, но никаких интересностей типа графических редакторов и игр)


Опять же, зависит от назначения. Для нас SVG парсер просто необходим. Для игр он скорей всего нафиг не нужен (хотя… кто его знает) :)

Вот кстати есть такой документик о том когда стоит использовать Fabric, а когда нет — github.com/kangax/fabric.js/wiki/When-to-use-Fabric
object.left = 100; // new value
canvas.renderAll(); // need to mark object dirty before rendering
although this could be "fixed" via set:

object.setLeft(100); // object is marked as dirty
canvas.renderAll(); // object is rendered at a new location


Почему бы не так?
object.left = 100; // new value
object.top  = 100; // new value
object.redraw();


Ну там в принципе и искать не надо :) Все альтернативы уже известны. Можно конечно сократить буквально до `$super()`. Но у всего как плюсы так и минусы.

Ни в коем случае. У всех этих подходов есть два существенных недостатка:
1. Они оборачивают все методы во враппер
2. Они вызываются через другой метод

В итоге ухудшается производительность и осложняется дебаг (тяжело лазить по стеку вызовов). Мой же подход слегка иной. Я думаю вы сразу поймёте, когда я покажу пример:

atom.declare('MyClass', ParentClass, {

  initialize: function initialize () {
    initialize.previous.apply( this, arguments );
    // our code here
  }
  
});


Такого решения я нигде не видел.
Не так изящно, как в мутулз, конечно, но зато без его недостатков:

MyClass = new Class({

  Extends: ParentClass,

  initialize: function initialize (a, b, c) {
    this.parent( a, b, c );
    // our code here
  }
  
});


Важно понимать что Fabric не позиционирует себя как framework для создания игр — у нас нет абстракций для работы со спрайтами, звуком, collision detection, и т.д. Поэтому, например, не было особого приоритета оптимизировать тысячи объектов на канвасе. Однако для удобной работы с коллажами Fabric по-моему идеален

Вообще согласен, всегда необходимо помнить цели и задачи. Но collision detection вам таки понадобится для учёта отрисовок)
initialize.previous.apply( this, arguments );


Да, интерeсный подход. В принципе всегда можно проверить если вызов супер метода присутствует в теле функции. Если да — заворачиваем, нет — нет. Я в Fabric именно так и делаю.

Для этого используем так называемое function decompilation (function(){...} + '') и просто смотрим если присутствует наш keyword ($super, callSuper, и т.д.).

Это де факто стандарт, и присутстует практически во всех существующих движках. В идеале можно перестраховаться и добавить feature test проверяющий что действительно работает. Если нет, то заворачиваем все функции подряд (что на практике не должно произойти вообще).
Тем не менее стек вызовов всё-равно смотрится отвратительно)
Хотя, скажу откровенно, подача фреймворка у вас отличная. Сайт, группа, хорошо оформленные доки. LibCanvas это откровенно не хватает.
Проблема в том, что родной canvas API ужасно низко-уровневый

Вы так шутите что ли? ))
А вообще (как-то мысль нагрянула) что разных фреймворков для канваса уже больше чем игр. Кстати на вашей фабрике есть готовые игры на которые бы приятно было бы глянуть?
Вы так шутите что ли? ))


Ну если сравнивать с попиксельным рисованием, то может быть и шучу :)

На самом деле, очень много повторяющихся конструкций и абстракция действительно помогает.

Из игр, я видел только тетрис — end3r.com/games/fabrictetris/
Для меня ужасно низко-уровневый это 2д в опенГЛ. А канва это так… душой отдохнуть))
Спасибо, хорошая статья. Когда будет следующая? Очень хотелось бы услышать про события, т. к. намерен использовать эту штуку для создания редактора карт.
Спасибо, жду с нетерпением новых статей. А третья часть есть? Хотя бы на английском, а то на сайте не нашел.
О, спасибо. Вопрос, на который не нашел ответа в доках — как скроллить канву? Нет, можно конечно вручную сдвигать координаты, но… Сами понимаете, хочется красоты и порядка:)
Встроенной поддержки для таких вещей как прокрутка всего канваса, увеличение или поворот — пока нет. Но в форуме можно найти несколько решений — groups.google.com/forum/#!forum/fabricjs
UFO just landed and posted this here
все трансформации проходят только относительно центра объекта?
я бы хотел трансформации по выбранной точке делать — есть возможность?
ну вот скажем я беру кубик с левой стороны и тяну вбок — чтобы правая сторона оставалась на месте, а левая двигалась только… вытягивать в нужную сторону как бы…


Да, это был один из самых популярных запросов :) Начиная с 1.0 версии, теперь именно так всё и работает. Вот посмотрите fabricjs.com/kitchensink/

еще интересно что манипулятор трансформации именно скейлит объект, в итоге обводка объекта тоже скейлится и вытягивая квадратик получаешь по бокам другую толщину контура.
есть ли возможность при таскании за манипулятор менять не скейл объекта, а его размер? то есть width и height??


Пока нет. Но изменение габаритов углов и краёв — это не хорошо, будем исправлять.

и есть ли кривые безье? а то квадратики да кругляки как то уже не радуют, хочется гладких кривых управляемых…


Конечно! Я же даже в конце статьи упомянул.

и все сказанное можно еще к группам применительно спросить — выделяем 3 объекта и тягяем их сбоку — чтобы все не скейлились, а изменяли свои размерности и все трансформации шли по некому пайвоту. лучше всего чтобы именно как вытягивание работало все — то есть двигаем левую границу, все остальные на места остаются…


Ну да. Посмотрите, именно так сейчас и работает.
Sign up to leave a comment.

Articles