Программная анимация
Автор: Евгений Рыжков Дата публикации:
Последнее обновление:
Задача
Выполнить анимацию без использования фреймворков.
Решение
HTML:
<div id="test"></div>
CSS:
#test { position: relative; left: 0; width: 50px; height: 50px; background: #000; }
window.onload = function(){ var block = window.document.getElementById("test"), // элемент anim, // таймаут start, // время старта now, // текущее время duration = 1000, // продолжительность from = 0, // стартовая позиция to = window.innerWidth/2, // финишная позиция progress = 0, // прогресс анимации x; // позиция в текущий момент времени // закон приращения аргумента (easing) function delta(param){ return param; }; // рендер function render(){ now = new Date().getTime(); progress = (now-start)/duration; x = (to - from)*delta(progress) + from; test.style.left = x+"px"; // если не конец выполняем анимацию еще if (progress < 1) anim = setTimeout(arguments.callee, 0) // иначе заканчиваем анимацию else { clearTimeout(anim); progress = 0; }; }; window.onclick = function(){ start = new Date().getTime(); render(); }; };
Индикатор статуса анимации — progress, изменяется в интервале от 0 до 1. На базе этого значения высчитывается положение анимируемого объекта. Если анимация не закончена, функция render() вызывается снова и снова через таймаут.
Оптимизация
Производители браузеров побеспокоились об оптимизации процесса анимации и предоставили нам API, которое помогает уменьшить число reflow и repaint. Имя ему RequestAnimationFrame.
Довольно показательный пример.
Немного изменяем JS код:
window.onload = function(){ var block = window.document.getElementById("test"), // элемент anim, // таймаут start, // время старта now, // текущее время duration = 1000, // продолжительность from = 0, // стартовая позиция to = window.innerWidth/2, // финишная позиция progress = 0, // прогресс анимации x; // позиция в текущий момент времени // закон приращения аргумента (easing) function delta(param){ return param; }; // рендер function render(){ now = new Date().getTime(); progress = (now-start)/duration; x = (to - from)*delta(progress) + from; test.style.left = x+"px"; // если не конец выполняем анимацию еще if (progress < 1) anim = setAnimation(render) // иначе заканчиваем анимацию else { clearAnimation(anim); progress = 0; }; }; window.onclick = function(){ start = new Date().getTime(); render(); }; }; //requestAnimFrame window.setAnimation = (function() { return window.requestAnimationFrame|| window.webkitRequestAnimationFrame|| window.mozRequestAnimationFrame|| window.oRequestAnimationFrame|| window.msRequestAnimationFrame|| function(/* function */callback, /* DOMElement */element) { return window.setTimeout(callback, 1000 / 60); }; })(); //canсelRequestAnimFrame window.clearAnimation = (function() { return window.cancelRequestAnimationFrame|| window.webkitCancelRequestAnimationFrame|| window.mozCancelRequestAnimationFrame|| window.oCancelRequestAnimationFrame|| window.msCancelRequestAnimationFrame|| function(id){clearTimeout(id)} })();
Т.к. API еще находится в стадии разработки, то приходится писать длинное условие "прощупывания" поддержки метода. Для браузеров, которые не поддерживают RequestAnimationFrame, в итоге используется setTimeout.
Материалы
- Основы программной анимации на JavaScript
- Переменная arguments
- Продвинутые анимации с requestAnimationFrame