«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
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 52

    • НЛО прилетело и опубликовало эту надпись здесь
        +7
        svg таки вектор, а это растр. то, что в свг есть такие возможности, не значит, что они не нужны здесь.
        • НЛО прилетело и опубликовало эту надпись здесь
            +2
            и чем же лучше? кто сказал, что быстрее?
            • НЛО прилетело и опубликовало эту надпись здесь
        –5
        «Осел не поддерживается. Опера, Хром и Фокс — работает» — куда же без осла в наше не лёгкое время?
          +1
          иногда бывает можно.
            –1
            Ох уж, это редкое «иногда»…
          +4
          За Вашей библиотекой — будущее. Javascript vs Flash 1:0 ;)
            +1
            спасибо)
              +3
              Месяц назад в своем блоге я написал заметку про перспективы клиентских технологий в веб. В заметке я как раз предсказывал появление подобного фреймворка. И вот — он появился. Желаю успехов в разработке и не бросать начатое дело :)

              Заметку можно глянуть тут: torqueo.net/client-technologies-perspectives-in-web/
                0
                как в воду глядели)
            • НЛО прилетело и опубликовало эту надпись здесь
                +1
                на минуточку Javascript = Actionscript = ECMAscript 4;
                +1
                Здесь «Фигуры. На базе CanvasLib.Shapes.*» вы накосячили, забили закрыть тег .
                  0
                  пофиксил
                  +1
                  А чем Вам не подходит Raphael?
                    +2
                    ну… он все-таки SVG, а не Canvas
                    –5
                    Как это использовать на друпал-сайте? Я не программист, сайт по кускам собираю, и чувствую необходимость встроить такую симпатичную иконку (с воскл.знаком — что-то типа «Важно» или «Внимание») на сайт, по клику на которую выползет попап-окошко с текстом и ссылкой.
                      –2
                      Я настолько не программист, что не знаю, что такое фреймворк.
                        +2
                        я так понимаю, что для этого лучше подойдет все таки JQuery…
                          +2
                          Объясните, что это? Не вижу смысла в минусах, я за помощью обратился.
                            +3
                            я не минусовал. видимо. людей удивил столь низкий урвоень клиентского программирования, словно вы пошутили. на хабре это редкость. пройдите хоть сюда: habrahabr.ru/blogs/jquery/38208/
                              +2
                              <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
                                Только в вашем случае подключение скриптов нужно расположить либо после вывода элементов img#warning и div#dialog, либо же выполнить код второго скрипта как обработчик события document.ready в любом другом месте (например, в head):

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

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

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

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

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

                                            Но если мы точно знаем, что не будет использовать прототипы (Prototype, MooTools etc.), то можно писать просто for in.
                                              0
                                              я вспомнил. это было при создании 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
                                                Не сталкивался с такой проблемой, да и перебираю обычно хэши.
                                                Загрязнение стандартных классов — это очень некрасиво.

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

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

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

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

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

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

                                          Самое читаемое