Senior JS Developer
0,6
рейтинг
19 марта 2010 в 09:14

Разработка → ProgressBar — Javascript Canvas2d

HTML*

Здравствуйте. Последнее время я достаточно часто имею дело с JavaScript-canvas, особенно написание всяких игрушек, которые требовательны к трафику в силу необходимости загрузки множества картинок.
Обычно сначала загружается около 50-100кб сжатого JavaScript, после чего — энное количество картинок(например, 500кб, 2мб, 10мб и т.п.) и только после этого запускается сама игра. Можно, конечно, загружать по ходу, но отсутствие текстур врядли порадует игрока.
Потому я решил, что необходимо сделать какой-то приличный, симпатичный, легко-настраиваемый(чтобы быстро менять от проекта к проекту) прогресс-бар, но, обязательно без использования картинок. Под катом исходники под лицензией LGPL, небольшая инструкция, как это сделать и внизу статьи — ссылка на результат.

Сразу скажу, я не претендую на какие-то особые восхищения уникальностью и гениальностью кода. Код прост и без особо хитрых приёмов, но, думаю, новички вполне смогут найти для себя что-то интересное.

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


Во время работы с Canvas практически не требуется работа с DOM, потому стандартные мощные JS-Фреймворки, а-ля Jquery (который я очень люблю) излишни и, при этом, не покрывают необходимых потребностей. Я использую собственный мини-фреймворк, который предоставляет легкую обертку над DOM, расширения стандарных примитивов и кое-что еще ;) Файл utils.js.
Также у меня есть файл CanvasExpander.js, который слегка расширяет исходный context.getContext('2d');. Кое-какие функции нам понадобятся…

Хочу обратить внимание на класс utils/Trace. Мне был необходим вывод объектов, так как firebug не работает в Firefox3.7a. А еще он, в отличии от Файрбага, позволяет выводит информацию в одном и том же блоке, что достаточно полезно при, например, выводе количества FPS. Отключается он одинарным или, иногда, двойным щелчком по блоку.
var tr = new Trace();
setInterval(function () {
    
tr.trace(fps);
}, 
20); 

На его базе сделан класс FpsMeter. Просто каждый раз перед началом кадра вызываем метод frame() объекта и оно будет нам высвечивать средний fps за последние n кадров (передается параметром при создании, смотрите код для примера)

Интерфейс


Сначала давайте определим интерфейс. Как обычно у меня выглядит загрузка картинок:
var id = new ImageDownloader({
    
wallBrick "/images/walls/brick-512px.png",
    
wallWood "/images/walls/wood-256px.png",
    
sectoid "/images/aliens/grey-64-256px.png"
    
// And So On
});
setInterval(function () {
    if (
id.ready) {
        
outputMainData(id.getImages());
    }
}, 
20);


Это позволяет мне не замысливаться над урлами, а в коде использовать только имя картинки. Например Images['wallBrick']. Но так как мы хотим добавить прогрессБар, необходимо немного расширить интерфейс, добавив публичное свойство progress (корректнее было бы через геттер, конечно, но это сейчас не так важно)
setInterval(function () {
    if (
id.ready) {
        
outputMainData(id.getImages());
    } else {
        
progBar.setProgress(id.progress).draw();
    }
}, 
20);


Исходные коды ImageDownloader нас не особо интересуют, потому лично я написал заглушку, плавно поднимающую прогресс с нуля до 100% за 3,4 секунды.

Создаем progressBar


Что напишем в методе draw()?
Вызывая наш класс программист не знает, что творится внутри, потому он ожидает, что ничего с его настройками не случится и context.fillColor или т. п. останется прежним. Для этого мы сохраним поля с помощью context.saveValues();, а после рендеринга восстановим исходные значения с помощью context.loadValues(); ( CanvasExpander.js ).
this.drawBorder(); нарисует обёртку для нашего прогрессБара:

Теперь сама линия. Допустим, картинки загрузятся за 5 секунд. А наш холст перерисовывается каждые 0.02 секунды (50 fps). Таким образом, линия перегенерируется с нуля за это время 250 раз. Намного выгоднее создать отдельный буфер, где сгенерировать эту линию, а после этого просто выводить соответствующую часть на экран. Мы создадим еще один элемент Canvas, в который будем её рисовать, а после этого брать (метод ProgressBar.createBuffer();), мы можем посмотреть результат, если раскомментируем строку присоединения к body в методе)
Теперь, когда мы её нарисовали, можно безболезненно её использовать каждый кадр:
ctx.drawImage(line0width progheight,
          
c.x+1c.y+1width progheight);

Где line — это хтмл-элемент canvas.


Пересчитывание стилей


Каждый раз, когда меняется стиль нашего прогрессБара вызывается метод countSizes, который все числа меньше нуля считает процентами. Это позволит выглядеть нашему прогрессБару одинаково, какого бы размера элемент Canvas ни был. Сначала высчитывается ширина в пикселях. Допустим, ширина Канваса — 800пикселей, а style['width'] = 0.8. Тогда ширина прогрессБара будет 640 пикселей. Все остальные величины зависят или от ширины прогрессБара или от высоты ('blendHeight', 'blendVAlign', 'textSize', 'textVAlign'). Это позволит мастабироватся вместе с элементом, не меняя пропорций.

Сообственно скрипт


Все исходники — внутри, не обфусцированы, лицензия: LGPL.
Павло @TheShock
карма
227,7
рейтинг 0,6
Senior JS Developer
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (31)

  • +8
    Очень был удивлён тому, что стиль на скриншоте один в один как у меня сейчас на убунте! Грешным делом испугался даже :-D Интересно так, давно думаю, что JS и Canvas это хорошая замена флешу, а так как под линуксами адекватного флеша нет (точнее среды для разработки), то хочется начать заниматься канвасом! Ваши скрипты могут стать примерами.
    • +9
      спасибо, безумно приятно слышать. я очень надесь на развитие Canvas и WebGL и стараюсь сделать всё, что в моих силах, чтобы принести этой технологии максимум признания.
      На счет того, что как в Убунте — если перейти по ссылке, то можно увидеть, что стиль так и называется — Ubuntu. Из предустановленных есть еще kUbuntu, openSuse и просто черный.
      • НЛО прилетело и опубликовало эту надпись здесь
  • +3
    класно! кстати да, явно не хватает хорошего фреймворка над канвасом, что-то типа универсального, как в dojo.fx.
    Маленькая ремарка, в FF 3.7, да и в остальных тоже, Firebug тоже работает :)
    • +1
      у меня были мысли написать фреймворк для создания игр с изометрческой проекцией и с видом сверху, но как-то, все-таки, руки не дошли. хотя, может, всё еще впереди…

      в FF 3.7 [...] Firebug тоже работает

      Я догадывался, что там просто надо строчку максимальной версии поменять. Но они это уже сделали вчера:
      We also set the maxVersion back to FF 3.7
    • +3
      Спасибо за статью! вот кстати кому интересно www.canvasdemos.com туториалы, и да еще, классный сайт у автора :)
    • 0
      Попробуйте http://raphaeljs.com/
  • +2
    Хорошая работа, слежу за творчеством )
  • НЛО прилетело и опубликовало эту надпись здесь
    • +2
      (function () {
          return "done";
      })();
      • 0
        Слишком мало вложений.
    • НЛО прилетело и опубликовало эту надпись здесь
  • +3
    Говорю себе: вот же оно, счастье! Так ведь нет, навязывают всякое говно.
    (только что открыл сайт где видео было в сильверлайте, не удержался… накипело)
  • +5
    Все бы использовали canvas, да вот только есть на свете IE :-(
    • +2
      Специально для счастливых обладателей IE есть API написанная гуглом explorercanvas
    • +1
      Люди для того, чтобы поиграться в ВоВик или Линейку качают сотни мегабайт. Неужели им так тяжело будет скачать 5 мегабайт альтернативного браузера, если они захотят поиграть в игрушку?)
      • +2
        линейку или вовик ставят обычно дома, чтобы спокойно рубиться ночами напролет. А в онлайновые игрушки чаще всего играют тогда, когда нужно скоротать (не)много времени, например, на работе :-) И не факт, что всегда есть возможность поставить/запустить другой браузер (политика партии: пиратский WinXP+sp1 с врезанным насмерть IE-6 + касьянс посынка). ну, всякое бывает.

        Ладно, я придираюсь.

        Но! Знаете ли, хотелось бы использовать canvas на всяких сайтах, при чем тут игрушки вообще? К автору статьи может быть и придет пользователь со свежим Firefox/Opera/Chrome, а ко мне заказчик придет со своим IE6 и будет долго ругаться, почему все не так, как задумывалось на макете. И придется делать все по-старинке.
        • 0
          если на работе нельзя устанавливать своё ПО:
          portableapps.com/apps/internet/firefox_portable

          при чем тут игрушки вообще

          прочтите первое предложение статьи ;)

          но доля истины в ваших словах есть.
          я очень надеюсь на поддержку Канваса с 9-ом ишаке.
          • –2
            Придираться я могу очень долго, вы уж простите :-)
            прочтите первое предложение статьи ;)

            А первое предложение статьи — «Здравствуйте».
  • +9
    overkill — достаточно двух вложенных дивов — внешний с бордером и фиксированной шириной. Внутренний — с цветным бэкграундом и шириной от 0 до 100% верхнего блока — вот и весь прогресс бар. Работать будет абсолютно везде.
    • +2
      ну это если обычный сайт. а если все приложение написано на Канвас, то нету смысли парится. На ХТМЛ оно будет хуже выглядеть и при это больше головной боли.
      • –2
        чем это оно хуже будет выглядеть? маркетингом?
        • 0
          тем, что прогрессбар на plain-html без картинок намного тяжелее сделать симпатичным, чем на канвасе)
          • –1
            div`у (внутреннему) за ночь стало невозможно background-image поставить?

            вы как-то гвозди забиваете не тем
            • +1
              а если бы вы почитали топик, то не несли бы чушь.
  • 0
    дружное вау-вау-вау в комментариях доставляет. почему opengl не используем, а?
    • 0
      а зачем?
      • –1
        затем же, зачем и canvas здесь
        • 0
          перед тем, как умничать хотя бы поверхностно ознакомтесь с темой. в вашем случае достаточно будет прочитать! первый! абзац статьи
  • 0
    Кстати вот у меня minefield версии 3.7pre, и Firebug работает, а все дело в волшебных пузырьках под названием extensions.checkCompatibility.3.7a = false в about:config :)
    • 0
      good, спасибо за совет)

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