Программная анимация
Дата публикации: 09.03.2012
Задача
Выполнить анимацию без использования фреймворков.
Решение
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
