Предыдущий ролик Следующий ролик  

Видео урок: Двойная буферизация

HTML5: графика и анимация с Canvas

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

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

Давайте продолжим и создадим такую анимацию. В редакторе я открываю начальный файл, и вот snippets, раздел Double Buffer Animation, копируем все это. И это код инициализации. Вот canvas и контекст страницы, вот он, наш canvas. Но у нас также есть переменные для buffercanvas, и это canvas, который будет создан вне экрана. У нас есть массив, содержащий все снежинки, у нас есть таймер, который используется для анимации каждой снежинки, а также у нас есть переменная, которая отслеживает максимальное число снежинок, которые будут показаны на экране. Сохраняем.

Давайте скопируем функцию инициализации. Копируем ее и вставляем. Функция инициализации будет вызвана тогда, когда загружается документ, вот она. Когда документ загружен, мы получаем ссылку на canvas и его контекст, находящийся на странице. Мы используем эту информацию, чтобы создать буферный canvas. Вы видите, что мы используем DOM документа прямо здесь, чтобы создать новый элемент canvas, а затем получить его контент.

Этот canvas не находится на странице; он находится в другом месте, но мы можем использовать его для рисования. Как только мы создали buffercanvas, мы устанавливаем его ширину и высоту на ширину и высоту canvas, который находится на странице. Как только мы это инициализировали, мы устанавливаем таймер интервала, который через определенное время выводит снежинки на экран. Каждые 200 миллисекунд, то есть, каждую пятую долю секунды, добавляется новая снежинка. Далее идет функция Draw, которая рисует начальный контент, а затем идет анимационный таймер, который вызывается каждые 30 миллисекунд. Давайте сохраним все это.

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

То есть, она будет перемещать каждую снежинку, учитывая скорость полета, направление и так далее. Как только объекты будут перемещены на новую позицию, мы вызываем функцию Draw, которая обрабатывает фон и копии контента buffercanvas и переносит их в canvas на экране. Так, теперь у нас все на месте, сохраняем и копируем логику поддержки. У нас есть две эти функции, addFlake и blank.

Копируем и вставляем их сюда. Также копируем функции Update и Draw, и сейчас мы посмотрим, что они делают. Я отправляю все это вот сюда. Как я уже упоминал ранее, у нас есть Update и Draw, а затем мы копируем некоторую логику поддержки. Последнее, что нам нужно скопировать, это логика создания снежинок.

Давайте скопируем это, и вы видите, что здесь есть addFlake, и мы вызываем новый объект Flake. Копируем все это и вставляем. Вот так все работает. Помните, здесь у нас есть таймер, который по времени добавляет снежинки на экран, а вот и эта функция.

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

У каждой снежинки есть позиция x и y, это вот эти две линии. Снежинка будет нарисована в самом верху canvas, и мы инициализируем ее на -10 пикселей, так что изначально она появляется вне canvas. Затем мы случайным образом выпускаем каждую снежинку по оси X в canvas, так что у них будут разные начальные позиции, когда они начинают падать. Для создания случайного числа мы используем встроенную библиотеку JavaScript Math, и функция random дает нам случайное число между 0 и 1.

Мы умножаем его на ширину canvas. Таким образом мы получаем случайное число, которое идет от нуля до самой правой стороны canvas, а затем мы просто используем функцию округления, чтобы округлить это число. Мы инициализировали позицию x и y для каждой снежинки, но мы не хотим, чтобы снежинки просто падали вниз, ведь это не совсем реалистично. Мы хотим, чтобы снежинки падали плавно. У нас есть свойство drift, и опять же, у нас есть случайное число между 0 и 1, и мы каждый раз добавляем его к позиции x, чтобы снежинки плавно падали.

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

Также нам нужно добавить каждой снежинке ширину и высоту. Добавляем снежинке ширину, и опять же, мы ведь хотим быть реалистичными, поэтому снежинки будут разных размеров, и у нас есть случайное число между 0 и 1, умноженное на 3, таким образом мы получаем ширину между 0 и 3. Но снежинки не могут быть с шириной в ноль, и нам нужно убедиться, что каждая снежинка видима, так что мы добавим сюда 2. Ширина наших снежинок будет колебаться от 2 до 5. А чтобы сделать их квадратными, высоту мы установим на ширину.

После того как мы создали снежинки, давайте посмотрим, что делает функция Update. Запомните, все время в цикле анимации сначала мы делаем обновление, а затем рисуем. По логике Update для всех снежинок в массиве мы обновляем их позиции, основываясь на их свойствах. Мы рассматриваем каждую снежинку, чтобы увидеть, является ли значение y меньше значения canvas; другими словами, что она не достигла низа canvas. Если это так, мы обновляем позицию снежинки.

Мы обновляем вертикальную позицию, добавляя скорость к любому значению y, и тогда снежинка будет падать вниз. Если это так, то снежинка падает в самый низ canvas - другими словами, значение y больше высоты canvas - и тогда мы уменьшаем значение до -5. Тогда снежинка перемещается снова на верх canvas. Мы обновили позицию y. Теперь нам надо обновить позицию x. Помните, что позицию x нужно обновить, потому что мы не хотим, чтобы снежинки падали просто вниз; мы хотим, чтобы они кружились.

Во время цикла анимации мы добавляем величину кружения для снежинки к позиции x. Как и с логикой для позиции y, нам нужно посмотреть, является ли позиция x больше, чем ширина canvas, потому что снежинки летят в правую сторону canvas. Если это так, мы просто сбрасываем позицию x снежинки на ноль. И это будет делаться для всех снежинок в массиве. Когда эта функция завершает работу, тогда все снежинки перемещаются на новые позиции. Это обозначает, что наступает время для запуска функции Draw.

После Update теперь функция Draw отвечает за рисование всего в новой позиции. Для контекста я также делаю сохранение и восстановление - это не обязательно, но лучше всего вставлять эти функции. Я вставляю их сюда, если рисование вдруг произойдет до или после этой конкретной функции, если у меня есть сложная анимация, с которой я работаю. Первое, что мы делаем, это вызываем функцию blank, и эта функция, вот она, рисует ночное небо. Сначала мы рисуем задний фон глубоким фиолетовым цветом, и мы заполняем прямоугольник в buffercanvas, начиная с левого верхнего угла и заканчивая правым нижним.

И опять же, вы видите, что все рисование происходит в buffercanvas вне экрана, а не в canvas на экране. Итак, мы просто рисуем задний фон, а затем уже рисуем снежинки. Опять же, у нас есть цикл, который проходит по всему массиву снежинок, и для каждой снежинки мы устанавливаем fillStyle canvas для buffercanvas на white (белый). И для buffercanvas мы заполняем каждый прямоугольник, представленный каждой снежинкой.

У нас есть позиция x и y, а также ширина и высота каждой снежинки. И мы делаем это для всех 200 снежинок. Когда в buffercanvas все нарисовано, мы копируем все обработанное изображение, эту рамку, из buffercanvas в canvas на экране. Делаем мы это при помощи функции drawImage, которая предназначена для того, чтобы скопировать нарисованное в canvas из buffercanvas. Копируем мы все с левого верхнего угла до правого нижнего.

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

Помните, что область среза определяет регион, вне которого рисование не будет происходить, а внутри которого рисование происходит . Создаем область среза мы при помощи функции beginPath, и опять же, для buffercanvas - все рисование произойдет в buffercanvas. Мы заполняем buffercanvas прямоугольником от левого верхнего угла до нижнего правого. Затем мы рисуем дугу, и дуга будет нарисована в центре buffercanvas, так что мы делим ширину и высоту на 2.

Таким способом мы получаем центр. Радиус дуги будет 40% от высоты canvas. То есть, я иду на 40% во всех направлениях. А поскольку радиус является половиной диаметра, мы получим круг на 80% высоты canvas. И, наконец, мы вызываем функцию, которая говорит canvas, что рисование должно происходить в области, определенной этим кругом. Давайте сохраним и посмотрим, что произойдет. Обновим страницу. У нас есть круг.

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