HTML5

индекс
61,16

«LibCanvas» — фреймворк для работы с Javascript Canvas

Здравствуй, Хабр! Думаю, люди, которые следят за моим творчеством, заметили, что я очень увлекся рисованием на Canvas в JavaScript. Возможно это немного излишне, но ничего не могу с собой поделать, уж очень нравится эта технология. Так нравится, что я аж буду выступать на конференции с докладом о ней (Пономаренко Павел).

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



Инструментарий


Я безумно люблю JQuery, Я считаю его идеальным фреймворком для работы с DOM и сейчас, когда я от него отказался в этом фреймворке — я еще раз убедился в этом. Но вне DOM, имхо, JQuery становится слишком громоздким и его философия только раздражает. Потому изначально я решил написать сообственный фреймворк. расширяя прототипы втроенных объектов и создавая базовые функции для работы с DOM. В определенный момент гугля в поисках интересных решений с наследованием в JavaScript я осознал одну истину: я пишу MooTools. Потому мною было принято решение, этот фреймворк был за пару часов изучен в достаточной для работы степени и я переписал весь, практически готовый проект (заодно отрефакторив) на новом фреймворке, о чем не жалею(Тем не менее для работы с DOM мне все равно намного приятнее JQuery). Кое-чего в MooTools мне таки не хватило, потому пришлось дорасширить втроенные объекты, добавив в их прототипы парочку методов)

Сообственно сам фреймворк


Все классы фреймворка находятся в пространстве имен LibCanvas.*. Я старался сделать его максимально изящным, исправив те недостатки, которые я смог заметить. Описание может показаться несколько сумбурным (в силу целой ночи программинга и того, что щас 7 часов утра, но с живым примером в конце станет яснее)

Оболочка


Я предлагаю работать с канвасом через некую универсальную оболочку, в которую уже встроен ImagePreloader, ProgressBar, fps-метер и другие фенечки.
new LibCanvas.Canvas2D($('canvas'))
    .
setFps(50// количество фпс, которое браузер постарается рендерить
    
.fpsMeter(20// включаем измеритель фпс. он будет брать последних N каждов, получать среднее значение и выводить количество fps
    
.setConfig({
        
background  '#EFEBE7'// этим фоном заливаемся весь холст
        
images      App.imagesList// пока эти картинки не загрузятся - канвас работу не начнет 
        
progressBar App.progressBarStyle // но чтобы пользователь не впал в ступор - покажем прогрессбар
    
})
    .
addElement(new App.MyFirstElement()) // Добавляем пару элементов в канвас
    
.addElement(new App.SecondElement()) // кажыдй кадр будет вызывать их метод .draw()
    
.start(); // сюда можно также передать ф-цию, в которой сделать дополнительные действия


Изменение Контекста


Мне всегда не нравилось во встроенном контексте две вещи: то, что функции возвращают undefined вместо this из-за чего нельзя делать цепочки вызовов и иногда функции с огромным количеством одинаковых аргументов, например:
ctx.drawImage(image1516123456123245);

Я создал свой контекст с бле с возвращаемым this, именованными параметрами и еще некоторыми плюшками. Замечу, что он обратно-совместим с обычным контекстом, хотя его использовать и не рекомендуется. Получить его очень просто:
$('canvas').getContext('2d-libcanvas')

Замечу, что отныне очень просто создавать свои контексты (если они вам нужны), для примера смотрите ./js/Libs/LibCanvas/Core/Context2D.js:
LibCanvas.addCanvasContext('2d-libcanvas'LibCanvas.Context2D);

На случай, если вы переопределили один из втроенных контекстов — всегда можно вызвать оригинальный:
$('canvas').getOriginalContext('2d');

Но контекст перетерпел кое-какие изменения и теперь стал удобнее. Например, теперь когда вы рисуете картинку — нет потребности догадываться, где какой параметр что значит (можно использовать любую пару from-to, from-size, to-size):
ctx.drawImage({
    
image images['ufo'],
    
crop  : {
        
from : [4080], 
        
to   : [120160]
    },
    
draw  : {
        
from : [8080]
        
size : [160160]
    }
})


И еще несколько косметических изменений:
ctx.fillAll('green'// Все полотно залили зеленым
    
.set('strokeStyle''red')
    .
stroke(new CanvasLib.Shapes.Polygon([
        [
231,  67],
        [
281,  67],
        [
317103],
        [
317153],
        [
281189],
        [
231189],
        [
195153],
        [
195103]
    ])) 
// обвели восьмиугольник
    
.arc({
        
circle : [1006040], // круг с центром в 100:60, радиусом 40 пикселей
        
angle  : [(5).degree(), (35).degree()] // c 5 по 35 градус
    
})

Замечу, что Number.degree() возвращает то вменяемое число градусов, которое было в Number, но в радианах, которые больше любит техника.

Фигуры


На базе CanvasLib.Shapes.* строится практически половина возможностей фреймворка. На данный момент там есть только три фигуры — Polygon, Circle, Rectangle, но со временем количество будет увеличиватся, например добавится RoundedRectangle, может еще что-то. Естественно можно создавать свои фигуры. Но важным при этом является реализация правильного алгоритма определения, находится ли определенная точка в пределах этой фигуры или нет. Поиск CanvasLib.Shapes.* в текущей версии находит 26 упоминаний.

Мышь и события


Самое интересное. В фреймворке реализованы события внутри элемента канвас а-ля тех, что мы используем в html. Изначально никак не задать обработку, например, mouseover на определенный объект внутри Канвас, это событие срабатывает только на сам html-элемент. Фреймворк позволяет работать с событиями очень легко и расширяемо. Достаточно подписаться на рассылку сообщений о событиях:
this.canvas.mouse.subscribe(this);

И при каждом событии будет вызывать метод event c именем события. На данный момент реализованы следующие: [click, mouseover, mousemove, mouseout, mouseup, mousedown], а также [away:mouseover, away:mousemove, away:mouseout, away:mouseup, away:mousedown] в случае, если событие произошло вне элемента. Для того, чтобы событие обработалось необходимо, чтобы элемент возвращал фигуру(getShape) у которой есть метод hasDot. То есть, например, если у нас есть круглая кнопка метод должен вернуть фигуру Circle "покарывающую" эту кнопку.
Иногда вместо того, чтобы обрабатывать евенты достаточно унаследоваться от LibCanvas.InterfaceElement, что позволит создавать объект, рендерящийся одним и з трех методов, зависимо от состояния:
elem.drawStandart(); // обычный метод
elem.drawHover();    // при наведенной мышке
elem.drawActive();   // когда объект нажат
, а также можно bind'ить и unbind'ить функции на события (как в том же ДжиКвери при работе с дом), что идеально для создания игр и GUI.

Пример


Заметьте, что само приложение - это только ./js/App/* и ./js/Start.js. Все остальное - фреймворк. Представте, насколько пришлось бы писать тяжелую логику, чтобы это обработать без фреймворка!
Желтая круглая фигня в правом нижнем углу - это EventsTester. Он подписан на события и выводит в правую панель состояние события. Если событие уже было хоть раз вызвано - меняются только координаты. Вы можете посмотреть, как для него выглядят все события, что происходят на хлсте.
Три фигни сверху - это LibCanvas.InterfaceElement, о котором я писал чуть выше. На них повешена через elem.bind('click'fn);
функция, которая показывает соответствующую картинку. Таким образом можна сделать, например, табы, как в JQueryUI. Или интерфейс какой-то игрушки)

Основные недоработки


1. Несколько функций в контексте остались со старым интерфейсом (например , setTransform). Я их не очень заю и на знаю, как их сделать удобнее для пользователя
2. Возможно недостаточно тестировалось
3. Осел не поддерживается. Опера, Хром и Фокс - работает

Благодарности


Спасибо за помощь моему другу greedykid, а также прекрасной девушке nutochka, (которая, между прочим, участвует в Miss IT) и помогала мне делать эту либу. Без вас я бы не справился.
Пользуясь случаем передаю привет маме и папе

Ссылки и лицензия


Лицензия - LGPL, скачать можно, как всегда на гуглокоде: code.google.com/p/libcanvas/
+62
8 апреля 2010, 08:07
105

комментарии (52)

+7
vflash #
вы изобретаете svg
+7
TheShock #
svg таки вектор, а это растр. то, что в свг есть такие возможности, не значит, что они не нужны здесь.
–2
vflash #
так или иначе то что вы делаете делает лучше и быстрее svg. особенно если дело касается событий и динамики.

+2
TheShock #
и чем же лучше? кто сказал, что быстрее?
0
vflash #
отрисовка линий, окружностей, тени итд. даже если вы добьетесь качества то какой ценой, производительность будет не той которую можно добиться «нативным» кодом. на мобильных устройствах разрыв будет виден еше сильнее.
события — поводите мышкой по канвасу, поглядите загрузку цп даже бе каких либо действий.

НЛО прилетело и опубликовало эту надпись здесь
+1
TheShock #
иногда бывает можно.
НЛО прилетело и опубликовало эту надпись здесь
+4
firsyura #
За Вашей библиотекой — будущее. Javascript vs Flash 1:0 ;)
+1
TheShock #
спасибо)
+3
Bonch #
Месяц назад в своем блоге я написал заметку про перспективы клиентских технологий в веб. В заметке я как раз предсказывал появление подобного фреймворка. И вот — он появился. Желаю успехов в разработке и не бросать начатое дело :)

Заметку можно глянуть тут: torqueo.net/client-technologies-perspectives-in-web/
0
TheShock #
как в воду глядели)
+8
MixailV #
Да, уже почти Flash 1.0
+1
fzn7 #
на минуточку Javascript = Actionscript = ECMAscript 4;
+1
SVN #
Здесь «Фигуры. На базе CanvasLib.Shapes.*» вы накосячили, забили закрыть тег .
0
TheShock #
пофиксил
+1
cry_san #
А чем Вам не подходит Raphael?
+2
TheShock #
ну… он все-таки SVG, а не Canvas
–5
nurakhov #
Как это использовать на друпал-сайте? Я не программист, сайт по кускам собираю, и чувствую необходимость встроить такую симпатичную иконку (с воскл.знаком — что-то типа «Важно» или «Внимание») на сайт, по клику на которую выползет попап-окошко с текстом и ссылкой.
–2
nurakhov #
Я настолько не программист, что не знаю, что такое фреймворк.
+2
TheShock #
я так понимаю, что для этого лучше подойдет все таки JQuery…
+2
nurakhov #
Объясните, что это? Не вижу смысла в минусах, я за помощью обратился.
+3
TheShock #
я не минусовал. видимо. людей удивил столь низкий урвоень клиентского программирования, словно вы пошутили. на хабре это редкость. пройдите хоть сюда: habrahabr.ru/blogs/jquery/38208/
+2
Volshebnyi #
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" 
type="text/javascript"></script>

<script type='text/javascript'>
  $('#warning').click(function(){
    $('#dialog').show();
  });
</script>


warning — id картинки с восклицательным знаком
dialog — id div'а с position:absolute
Подробнее тут.
+2
Akenfold #
Только в вашем случае подключение скриптов нужно расположить либо после вывода элементов img#warning и div#dialog, либо же выполнить код второго скрипта как обработчик события document.ready в любом другом месте (например, в head):

<script type='text/javascript'>
    jQuery(function ()
    {
        jQuery('#warning').click(function ()
        {
            jQuery('#dialog').show();
        });
    });
</script>

0
kirushik #
Вам вообще не нужен canvas для этого.
Советую попробовать библиотеку rightjs: ru.rightjs.org/docs/fx/slide
Внятная документация на русском, и быстрый код.
0
nurakhov #
Спасибо за ответы, буду пробовать.
+3
slik #
Предлагаю выложить на MooTools Forge
0
TheShock #
спасибо, так и сделаю вечером.)
+1
Xenkok #
а вообще поддержка ослов планируется?
+1
kirushik #
Бывает excanvas, эмулирующая работу canvas для IE.
Но там не всё так просто — Ie8 надо с ней переводить в режим рендеринга от седьмой версии, иначе не работает
+1
kirushik #
Ну и понятно, эмуляция не шустра.
Совсем не шустра.
Какие 50fps, о чём вы?
+2
Akenfold #
Когда в Internet-Explorer'е сделают поддержку canvas, тогда автоматически будет поддержка и этого браузера :)
0
Xenkok #
ну я имел в виду — excanvas.
–5
akzhan #
Если бы было плагином jQuery, было бы интересно.

А включать абсолютно левый каркас ради канваса как-то не алё.
–3
akzhan #
А самое неприятное, — это расширение прототипов базовых классов.

На редкость вредно, особенно для случая for (var key in something).
0
TheShock #
используя for (var key in something) вы можете непороться на неприятные аги даже бзе расширенных проттивов, что у меня раз и произошло
–2
akzhan #
Я пока не напарывался. Хотя достаточно плотно и долго работаю с Javascript.
+2
TheShock #
афаик, один из ослов у меня реагировал неадекватно. сам любил эту запись потому отказывался от расширения прототипов, но пришлось отказаться эж ибо не айс. в Мутулз (да и везде) лучше использовать или array.each(), или for (var i = 0; i<array.length; i++) про ДжиКвери я писал.
0
akzhan #
У меня есть крайне насыщенных for in проектов, полных AJAX и прочего, с аудиторией, начиная с MSIE 6, и всё работает как часы.

В jQuery полезна запись вида
$.each(collection, function(...)
{
});

Но если мы точно знаем, что не будет использовать прототипы (Prototype, MooTools etc.), то можно писать просто for in.
0
TheShock #
я вспомнил. это было при создании jQuery.keyboard. Мне пришлось переписать все for-in на for(var i =0; в определенный момент. с тех пор от for-in для массивов я отказался. даже несмотря на то, что он иногда работает корректно. тем более удобство от возможности рсширить встроенные объекты — больше. вот коммит:
code.google.com/p/jquerykeyboard/source/detail?r=8ffb066d055529311a0fbc599b4b26539af39f1f
вы же не думаете, что я это сделал от нечего делать просто? стандартные прототипы не расширялись(это не соответствует философии jQuery), а баг где-то вылез. решил себя поберечь. а в мутулз очень удобно:
['a''b''c'].each(function (elem) {
    
alert(elem);
}.
bind(this));


вообще я решил, что даже юзая ДжиКвери для DOM я буду также использовать сокращенный Мутулз (тот, что для серверов) для повышения удобства пользования. Прям как двойная колода в Magic The Gathering)))
0
akzhan #
Не сталкивался с такой проблемой, да и перебираю обычно хэши.
Загрязнение стандартных классов — это очень некрасиво.

Если бы это не влекло побочных эффектов, мне было бы пофиг.
0
Sannis #
Для этого случая Douglas Crockford настоятельно рекомендует писать внутри цикла проверку: www.jslint.com/lint.html#forin
На всякий случай, на будущее или по иной причине, но это стоит сделать даже если на данном этапе побочных эффектов быть не может.
А то решит вебмастер использовать на сайте prototype.js, а в циклах по массивам вылезут методы вдруг. Не комильфо.
+2
proxor #
Забавно вы назвали MooTools «левым» каркасом, а чуть ниже пишете, что «достаточно плотно и долго работаю с Javascript» :)
0
akzhan #
В большинстве компаний — Предусловие: во всех проектах компании используется jQuery;

Постусловие: Минимальное влияние на размер проекта.

0
proxor #
Смысл в другом. Если вы плотно работаете с JS, то не можете не быть знакомым с MooTools и знать хотя бы в общих чертах преимущества и недостатки этого каркаса. Вы же без заззрения совести назвали его «левым», что конечно не является адекватной оценкой и ставит под сомнение ваш профессиональный опыт. К тому же с Canvas можно вполне работать вручную, без всяких фреймворков, получится даже удобнее. Ну а если вы такой крутой, то без проблем напишете свою обёртку для Canvas к jQuery.
0
akzhan #
Что не отменяет вышесказанного. MooTools используется реже. На моей памяти я его видел только в одном проекте в работе.

А касательно писать свою обёртку для Canvas, — пока нет такой необходимости, меня устраивает Rafael (SVG+VML).
0
proxor #
Сами себе противоречите, Raphaёl ведь тоже — судя по вашей логике — «левый каркас» :) Впрочем, неважно. Каждый использует то, что ему нравится.
+2
theOnlyBoy #
Приведенный пример кросс-браузерно реализуется набором из 6 картинок и небольшим куском кода на том же jQ. Приведите что-то более действенное, анимацию, например, или интересную картинку из сложных полигонов.
+1
Akenfold #
Это ведь только пример. Есть графическая библиотека, можно обрабатывать события для созданных элементов. Что ещё нужно? С таким функционалом можно написать практически всё, что угодно, от небольших компонентов (кнопочек, списков, переключателей и прочего), до полноценной графической среды и даже небольшой игрушки. Но это, конечно, потребует времени и сил.

Я хочу сказать, что работа с графическими средствами — не самоё простое программирование. Но люди, которые работали с другими подобными инструментами, наверное, понимают, какие возможности стоят за базовыми методами, реализованными во этом фреймворке. Поэтому сделать «что-то более действенное» не так-то и просто.
0
theOnlyBoy #
С таким функционалом можно написать практически всё, что угодно...
Никто не спорит. Так покажите ж, что можно-то, качественная презентация — залог успеха.
–1
twenty #
Опять Flash придумывают…

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