Но это сейчас неважно, потому что я хочу презентовать фреймворк, который, хотя, только родился и еще не оброс функциональностью, но уже сейчас выглядит вполне завершенным, интересным и, главное, удобным.
Инструментарий
Я безумно люблю 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(image, 15, 16, 12, 34, 56, 12, 32, 45);
Я создал свой контекст с бле с возвращаемым 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 : [40, 80],
to : [120, 160]
},
draw : {
from : [80, 80]
size : [160, 160]
}
})
И еще несколько косметических изменений:
ctx.fillAll(`green`) // Все полотно залили зеленым
.set(`strokeStyle`, `red`)
.stroke(new CanvasLib.Shapes.Polygon([
[231, 67],
[281, 67],
[317, 103],
[317, 153],
[281, 189],
[231, 189],
[195, 153],
[195, 103]
])) // обвели восьмиугольник
.arc({
circle : [100, 60, 40], // круг с центром в 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/