Создание JavaScript-анимации
Двумерная и трехмерная анимация, создаваемая как традиционными, так и компьютерными методами, основана на одном и том же принципе: если ряд статичных изображений показать в достаточно быстром темпе, то человеческий глаз свяжет их вместе и примет за непрерывное движение.
Наиболее очевидный и распространенный вид анимации в компьютерной графике – это спрайтовая анимация.
Спрайт — графический объект в компьютерной графике, чаще всего растровое изображение, свободно перемещающееся по экрану.
Наиболее простой вид спрайта – это прямоугольное изображение. Для начального его определения необходимо задать размер и положение.
Перемещение спрайта по экрану определяет изменение координат его положения с течением времени.
Т.е. необходима функция, которая срабатывает с определенным интервалом времени. И такая функция в JS есть, и не одна.
- setTimeout позволяет вызвать функцию один раз через определённый интервал времени.
- setInterval позволяет вызывать функцию регулярно, повторяя вызов через определённый интервал времени.
setTimeout
Синтаксис:
var timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)
Параметры:
func|code - Функция или строка кода для выполнения. Обычно это функция. По историческим причинам можно передать и строку кода, но это не рекомендуется.
delay - Задержка перед запуском в миллисекундах (1000 мс = 1 с). Значение по умолчанию – 0.
arg1, arg2… - Аргументы, передаваемые в функцию.
Отмена через clearTimeout
Вызов setTimeout
возвращает «идентификатор таймера» timerId
, который можно использовать для отмены дальнейшего выполнения.
Синтаксис для отмены:
var timerId = setTimeout(...); clearTimeout(timerId);
setInterval
Метод setInterval
имеет такой же синтаксис как setTimeout
:
var timerId = setInterval(func|code, [delay], [arg1], [arg2], ...)
Все аргументы имеют такое же значение. Но отличие этого метода от setTimeout
в том, что функция запускается не один раз, а периодически через указанный интервал времени.
Чтобы остановить дальнейшее выполнение функции, необходимо вызвать clearInterval(timerId).
Пример анимации линейного спрайтового движения:
Файл index.html:
<html> <head> <meta charset="utf-8" /> <script src="test.js"></script> </head> <body onload="init()"> <canvas id="tutorial" width="500" height="500"></canvas><br /> <input type="button" onclick="start()" value="Пуск"> <input type="button" onclick="stop()" value="Стоп"> </body> </html>
Файл test.js:
var x; var Idint; var ctx; function init(){ x=10; ctx = document.getElementById('tutorial').getContext('2d'); } function draw() { ctx.fillStyle = 'white'; ctx.fillRect(x,10,50,50); x=x+3; ctx.fillStyle = 'blue'; ctx.fillRect(x,10,50,50); } function start() { Idint = setInterval(draw, 100); } function stop() { clearInterval(Idint); }
В действительности лучше отображать меньшее количество кадров в секунду, но сделать это количество постоянным. Дело в том, что наш глаз воспринимает небольшие отклонения в частоте, и несколько выпавших кадров режут глаз больше, чем более низкое количество кадров в секунду. Вот здесь на помощь приходит встроенный в HTML5 API requestAnimationFrame.
Преимущества requestAnimationFrame
requestAnimationFrame
дает браузеру возможность контролировать, сколько кадров он может обработать. Вместо того, чтобы требовать от браузера отображать кадры, которые в итоге выпадут, вы разрешаете ему показывать кадры тогда, когда они обработаны, и с постоянной частотой. Польза от этого двояка:
- Анимация выглядит более плавной, поскольку уровень кадров в секунду остается постоянной.
- Процессор не перегружается задачами по рендерингу, а может обрабатывать и другие задачи во время рендеринга анимации. Вообще браузер может определить тот уровень кадров в секунду, который будет оптимален для задач, которые браузер выполняет одновременно с анимацией.
Синтаксис:
var requestId = requestAnimationFrame(callback)
Такой вызов планирует запуск функции callback
на ближайшее время, когда браузер сочтёт возможным осуществить анимацию.
Пример анимации линейного спрайтового движения:
Файл test.js:
var x; var Idint; var ctx; function init(){ x=10; ctx = document.getElementById('tutorial').getContext('2d'); } function draw() { if (x<450) { ctx.fillStyle = 'white'; ctx.fillRect(x,10,50,50); x=x+3; ctx.fillStyle = 'blue'; ctx.fillRect(x,10,50,50); Idint = requestAnimationFrame(draw); } else {cancelAnimationFrame(Idint);} } function start() { Idint = requestAnimationFrame(draw); } function stop() { cancelAnimationFrame(Idint); }
Оптимальная реализация анимации объектов
Хорошим стилем создания каких-либо анимаций является разделение математических расчетов и прорисовки. Для демонстрации этого принципа создадим четыре функции: play, render, update и requestAnimFrame.
Функция update будет отвечать за математические расчеты: просчитывать координаты анимируемых объектов, проверять их на столкновения и пр.
Функция render будет отображать все необходимые объекты на холсте, т.е. заниматься прорисовкой.
Функция play поочередно вызовет render, update и requestAnimFrame.
Функция requestAnimFrame указывает браузеру на то, что необходимо произвести анимацию, и просит его запланировать перерисовку на следующем кадре анимации. В качестве параметра метод получает функцию, которая будет вызвана перед перерисовкой.
Структура кода с универсальной функцией requestAnimFrame приведена ниже:
function play(){ update(); // Расчеты render(); // Прорисовка requestAnimFrame(play); //Функция play должна повторяться, чтоб работала анимация } function update(){ } function render(){ } var requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { window.setTimeout(callback, 50); }; })(); play(); // Запуск анимации
Стоит обратить внимание, как реализована функция requestAnimFrame: для различных браузеров предлагается соответствующий вызов по имени функции, встроенной в их движок. В случае, если браузер не поддерживает ее, то вызывается универсальная функция setTimeOut.