Pull to refresh

ProgressBar — Javascript Canvas2d

Reading time 4 min
Views 6.2K

Здравствуйте. Последнее время я достаточно часто имею дело с 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.
Tags:
Hubs:
+80
Comments 31
Comments Comments 31

Articles