Создание анимаций на холсте

С помощью canvas JavaScript вы можете создавать анимацию, в которой ранее статические пиксели перемещаются или визуально изменяются в течение определенного периода времени. Чтобы сделать это правильно, нужно изучить несколько важных приемов. Узнаем, как создать анимацию в canvas!

Экскурс в анимацию

Вспомним, что такое анимация. В общем смысле анимация является визуализацией изменений, которые происходят в течение определенного периода времени. Рассмотрим это более подробно.

Начальное и конечное состояние

Если визуализация изменений является важной частью анимации, нам нужно создать несколько ключевых точек, чтобы с их помощью можно было сравнить, что же изменилось. Назовем эти ключевые точки начальным и конечным состоянием. Чтобы лучше понять, о чем идет речь, рассмотрим простой JavaScript canvas пример.

Допустим, что начальное состояние выглядит следующим образом:

interpolation_start

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

interpolation_end

Основываясь только на информации о том, как наш синий круг выглядит в начальном и конечном состоянии, можно ли определить эти изменения?

Изменилась позиция фигуры. Синий круг находился в левой стороне экрана. В конце он находится в правой стороне. Еще одно изменение — размер. Наш круг был маленьким, теперь он большой.

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

Интерполяция

На данный момент у нас есть два дискретных состояния во времени: начальное и конечное. Чтобы создать анимацию из того, что у нас есть, нужен плавный переход, который определяет все промежуточные состояния. Создание промежуточных состояний называется интерполяцией.

Интерполяция, которая происходит в течение заданного периода времени, будет выглядеть следующим образом:

interpolation_states

При рисовании на canvas в JavaScript вам нужно будет указать начальное состояние, конечное состояние, продолжительность и промежуточные состояния. После этого вы получите анимацию!

Анимация в canvas

В предыдущих разделах мы рассмотрели общие концепции построения анимации. Пришло время сосредоточиться на деталях.

Вначале холст полностью пуст. Назовем это начальное состояние кадр 1:

blank_frame_canvas_72

В этом пустом кадре вы рисуете то, что нужно вывести как часть начального состояния:

frame_1_filled_72

Вы рисуете все — от элементов на переднем плане до элементов, которые составляют фон. canvas ничего не делает за вас. Он любезно оставляет вам право на творчество.

После того как будете удовлетворены тем, как выглядит первый кадр, вы стираете все, что только что вывели. Теперь у вас есть новый кадр:

frame_2_72

В кадре 2, вы перерисовываете все, что было в кадре 1, но с небольшими изменениями:

frame_2_filled_72

В нашем JavaScript canvas примере между кадрами 1 и 2 фигура пятиугольника слегка поворачивается и перемещается в сторону и вниз. Все остальное выглядит так же. Теперь, если сравнить кадр 1 и кадр 2, можно увидеть эти небольшие изменения:

frames_adjacent

То, что вы только что задали — это промежуточное состояние. Чтобы создать плавную анимацию, нужно будет определить много промежуточных состояний. Вам придется повторить процесс рисования и очистки снова и снова для кадра 3, кадра 4, кадра 5 и так далее. В каждом новом кадре фигура пятиугольника будет незначительно изменяться по сравнению с предыдущим.

На следующей диаграмме показана последовательность кадров для этой анимации:

hexagon_moving_frames_72

Продолжайте создавать промежуточные кадры, пока не достигнете конечного состояния — окончания последовательности анимации. Когда все кадры быстро воспроизводятся один за другим, получается анимация.

Время реализации

В этом разделе с помощью мы создадим простую анимацию перемещения круга и посмотрим, как этапы рисования и JavaScript canvas очистки соответствуют строкам кода.

Добавление холста

Первое, что нужно сделать, это добавить элемент canvas, который будет содержать анимацию. Создайте пустую HTML-страницу и добавьте в нее следующий код HTML и CSS:

Пример простого холста

canvas {
    border: 10px #333 solid;
}

<div id="container">

</div>

Просмотрев эту страницу сейчас, вы должны увидеть пустой квадрат, очерченный темно-серой рамкой.

Рисование круга

Далее с помощью canvas JavaScript нужно нарисовать круг, который мы хотим анимировать. Внутри тега script добавьте следующие строки:

var mainCanvas = document.querySelector("#myCanvas");
var mainContext = mainCanvas.getContext("2d");

var canvasWidth = mainCanvas.width;
var canvasHeight = mainCanvas.height;

function drawCircle() {

}
drawCircle();

Мы создаем код шаблона для более быстрого доступа к элементу canvas и его контексту рисования. Хотя это довольно скучно, но совсем скоро будут происходить интересные вещи…

Внутри функции DrawCircle, добавьте следующий код, который собственно и будет рисовать на JavaScript круг:

function drawCircle() {
  mainContext.clearRect(0, 0, canvasWidth, canvasHeight);

  // цвет фона
  mainContext.fillStyle = "#F8F8F8";
  mainContext.fillRect(0, 0, canvasWidth, canvasHeight);

  // рисование круга
  mainContext.beginPath();

  var radius = 175;
  mainContext.arc(225, 225, radius, 0, Math.PI * 2, false);
  mainContext.closePath();

  mainContext.fillStyle = "#FFCC00";
  mainContext.fill();

  mainContext.lineWidth = 10;
  mainContext.strokeStyle = "#DCB001";
  mainContext.stroke();
}

Если все получилось, вы увидите круг, который при просмотре в браузере выглядит следующим образом:

canvas_anim_example_144

Теперь нам нужно вызвать clearRect:

mainContext.clearRect(0, 0, canvasWidth, canvasHeight);

Canvas метод JavaScript clearRect () отвечает за один из двух этапов анимации — за очистку. В качестве аргументов он принимает координаты прямоугольной области, которую нужно очистить.

Так как нам нужно очистить всю область canvas, мы задаем прямоугольную область, которая начинается в левом верхнем углу (0, 0) и имеет размер, указанный в значениях переменных canvasWidth и canvasHeight:

clearrect_lines_144

Поле запуска этого кода все пиксели в пределах прямоугольной области будут очищены. После этой строки остальной код относится к выводу круга на холсте.

Анимирование круга

Это замечательно, что теперь у нас есть желтый круг, который выводится на холсте, но на самом деле мы хотим анимировать этот круг, заставив его перемещаться слева направо. Если говорить более конкретно, мы хотим анимировать значение позиции х нашего круга, которая указывает на то, где он находится относительно горизонтальной оси. Для этого нужно немного изменить значение позиции х каждые несколько миллисекунд, чтобы создать промежуточные состояния анимации.

Это достигается с помощью цикла анимации. Это функция, которая вызывается через малые промежутки времени. Внутри нее задаются для каждого нового вызова изменения, которые нужно отобразить.

Чтобы создать цикл анимации, существует специальная функция requestAnimationFrame. Она отвечает и за то, как вызывается эта функция, и за то, чтобы она вызывалась повторно.

Функция requestAnimationFrame вызывает цикл анимации через равномерные промежутки времени. Но каждый вызов совпадает с тем, когда браузер прорисовывает экран. Это позволяет выполнять код внутри цикла анимации только тогда, когда это фактически отразится при обновлении экрана. Рисование на canvas JavaScript – новый уровень оптимизации, по сравнению с более традиционными циклами на основе таймеров типа setTimeOut и setInterval.

Попробуем requestAnimationFrame в действии! Добавьте следующую выделенную строку кода в самом низу функции DrawCircle:

function drawCircle() {
  mainContext.clearRect(0, 0, canvasWidth, canvasHeight);

  // цвет фона
  mainContext.fillStyle = "#F8F8F8";
  mainContext.fillRect(0, 0, canvasWidth, canvasHeight);

  // рисование круга
  mainContext.beginPath();

  mainContext.arc(225, 225, 175, 0, Math.PI * 2, false);
  mainContext.closePath();

  mainContext.fillStyle = "#FFCC00";
  mainContext.fill();

  mainContext.lineWidth = 10;
  mainContext.strokeStyle = "#DCB001";
  mainContext.stroke();

  requestAnimationFrame(drawCircle);
}

requestAnimationFrame вызывает функцию DrawCircle каждый раз, когда браузер обновляет страницу. Частота обновления зависит от всего остального, что происходит на странице, но обычно она перерисовывается порядка 60 раз в секунду (или каждые 16,67 миллисекунды). Соответственно, функция DrawCircle будет вызываться примерно 60 раз в секунду.

Но просто вызвать функцию requestAnimationFrame недостаточно. Нам нужно внести изменения, чтобы задать небольшое горизонтальное смещение круга при каждом вызове DrawCircle. Внесите в код изменения, которые выделены ниже серым цветом:

var xPos = -500;

function drawCircle() {
  mainContext.clearRect(0, 0, canvasWidth, canvasHeight);

  // цвета фона
  mainContext.fillStyle = "#F8F8F8";
  mainContext.fillRect(0, 0, canvasWidth, canvasHeight);

  // рисование круга
  mainContext.beginPath();
  mainContext.arc(xPos, 225, 175, 0, Math.PI * 2, false);
  mainContext.closePath();

  mainContext.fillStyle = "#FFCC00";
  mainContext.fill();

  mainContext.lineWidth = 10;
  mainContext.strokeStyle = "#DCB001";
  mainContext.stroke();

  xPos += 5;
  if (xPos > 1000) {
    xPos = -500;
  }

  requestAnimationFrame(drawCircle);
}

Теперь, просмотрев страницу, вы должны увидеть круг, который перемещается с одной стороны холста к другой. Вы только что успешно создали простую анимацию! Теперь рассмотрим, как строки кода создают анимацию canvas JavaScript.

Как работает анимация

Начиная с первой строки, мы объявляем и инициализируем глобальную переменную xPos:

var xPos = -500;

Переменная будет содержать горизонтальное положение нашего круга, и мы задали для нее начальное значение -500. Это объявление само по себе не влияет на положение круга, потому что на самом деле оно задается с помощью вызова метода arc:

mainContext.arc(xPos, 225, 175, 0, Math.PI * 2, false);

Первый аргумент метода arc определяет горизонтальное положение, в котором мы рисуем круг. Поэтому вместо того, чтобы задать для него фиксированное значение, мы передаем в этот аргумент переменную xPos. Если бы мы остановились на этом, все, что мы сделали, это нарисовали круг с горизонтальной позицией -500.

Анимация canvas JavaScript достигается за счет работы со значением xPos. Мы изменяем его каждый раз, когда вызывается функция DrawCircle, немного увеличивая. Это делается с помощью следующей строки кода:

xPos += 5;

Каждый раз, когда вызывается функция DrawCircle, значение xPos увеличивается на 5. Это изменение в сочетании с вызовом метода arc приводит к тому, что круг каждый раз смещается вправо на пять пикселей. В конце концов, xPos достигает такого значения, что круг скрывается с холста:

canvas_anim_partial_144

Значение xPos будет постоянно увеличиваться, если мы это не остановим. В этом случае наш круг промелькнет мимо только один раз и никогда больше не вернется в исходное положение. Чтобы наш круг не исчез навсегда, промелькнув один раз, мы сбрасываем переменную xPos, когда ее значение достигает 1000. Это задается следующим кодом:

if (xPos > 1000) {
  xPos = -500;
}

Когда значение xPos сбрасывается на -500, анимация фактически перезагружается. Значение xPos снова постепенно увеличивается на 5 с каждым вызовом функции DrawCircle, достигает значения 1000, а затем все начинается сначала со значения -500. На каком-то участке этого перемещения круг становится видимым в пределах холста. Все!

Заключение

Анимация canvas JavaScript — это интересно. Работа с ней похожа на создание анимации в 1990-е годы, когда нужно было заниматься рисованием элементов, очисткой экрана, перерисовыванием вручную.

Перевод статьи «Creating Animations on the Canvas» был подготовлен дружной командой проекта Сайтостроение от А до Я.