Что такое requestAnimationFrame?

Каждый, кто хоть немного пользуется javascript, пытался реализовать анимацию. Это могут быть меню-аккордеоны, выпадающие меню, что-то движущиеся, сделанное на canvas или svg или в крайнем случае снег, который летает на доброй половине сайтов перед новым годом ( не нравится мне он :P ). Лучшим решением в случае анимации, бесспорно будет css transitions и animations. Но, к сожалению на момент написания статьи, ie9 (последняя версия ослика) ничего о css анимации не знает, да и в некоторых случаях (анимация на canvas например), css никак не поможет.

Давайте рассмотрим типовую функцию, которую мы используем, когда реализуем анимацию:

Javascript

var step = function(){
  // описываем один шаг
}

setInterval(step, 100); // повторяем шаг через наждые 0.1с

или так:

Javascript

var step = function(){
  setTimeout(step, 100); // циклически выполняем шаги один за одним, вызывая функцию саму из себя
  // описываем один шаг тут
}
step(); // выполняем шаг в первый раз 

setInterval и частота кадров

Плавность анимации зависит от частоты кадров. Частота кадров измеряется в “кадрах в секунду” (FPS – frame per second). Фильмы и видео обычно делают с 24fps – 30fps. Чем больше это число, тем более гладкой кажется анимация. С другой стороны – чем больше fps, тем больше требуется ресурсов процессора, что может привести к подвисанию и пропуску кадров. Поскольку большинство экранов имеет частоту обновления 60 Гц – fps к которому надо стремится равен 60-ти.

Немного математики и идеальный интервал 1000ms / 60(fps) = 16.7ms

т.е.

Javascript

setInterval(step, 17); 

И что? В чем подвох?

Что не так с этой функцией?

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

Кстати умный Chrome делает setInterval и setTimeout равным 1fps в скрытых вкладках.. но, насколько я знаю, так пока делает только chrome :(

Во-вторых, SetTimeout требует перерисовки страницы не в то же время, когда это делает ваш компьютер (а он делает это регулярно). Это означает, что ваш бедный браузер должен синхронизировать вашу горе-анимацию с обновлением всего экрана, и если ее частота не синхронизирована с обновлением всего экрана, это может потребовать больше вычислительной мощности. А это загрузка процессора, нагрев, шум куллера, расход батареи мобильных девайсов.. и т.п

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

requestAnimationFrame – путь к спасению!

Чтобы преодолеть эти проблемы, Mozilla (создатели Firefox) предложил функцию requestAnimationFrame, которая впоследствии была принята и усовершенствована командой WebKit (Chrome и Safari). Она обеспечивает встроеный API для запуска любых типов анимации в браузере (DOM элементов, canvas, WebGL и др.)

Javascript

function step() {
  requestAnimationFrame(step);
  // описываем один шаганимации тут
}
step();

Шикарно! Это же, как setTimeout, приведенный выше, но с requestAnimationFrame вместо него!!! При желании вы можете передать еще один параметр – анимируемый элемент: requestAnimationFrame(step, element);

Однако, как вы могли заметить, мы не указали интервал. Как часто бдет вызываться наша функция? Все зависит от частоты кадров вашего браузера и компьютера (обычно это 60 кадров в секунду). Ключевым отличием является то, что вы просите браузер выполнить функцию (в нашем примере step) при первой возможности, а не с заданным интервалом. Еще одно достоинство такого подхода в том, что браузеры могут снизить requestAnimationFrame в зависимости от нагрузки, видимости элемента и состояния батареи.

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

Если вы используете requestAnimationFrame, все ваши анимации будут гладкими и красивыми, синхронизированными с вашим графическим процессором (GPU) и съедающими гораздо меньше ресурсов центрального процессора (CPU).

Ух ты! Клево! И что все так безоблачно? .. или есть проблемы?

Ну, да, проблемы пока есть, но совсем пустяковые. Это совсем новый API и.. для Chrome и Safari – это webkitRequestAnimationFrame. Соответственно в Firefox это mozRequestAnimationFrame. Microsoft (Internet Explorer 10) будет поддерживать msRequestAnimationFrame.

Чтобы справится с этим, Eric Möller (Opera), Paul Irish (Google) и Tino Zijdel (Tweakers.net) создали polyfill приведенный ниже:

Javascript

(function() {
    var lastTime = 0;
    var vendors = ['ms', 'moz', 'webkit', 'o'];
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] 
                                   || window[vendors[x]+'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame)
        window.requestAnimationFrame = function(callback, element) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 
              timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };

    if (!window.cancelAnimationFrame)
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
}());

Вставляем в начало кода и requestAnimationFrame работает как родной. В браузерах, где он вообще не поддерживается, будет изпользоваться setInterval

Но что делать, если я хочу установить частоту кадров?

Проблемы заключается в том, как контролировать fps для requestAnimationFrame, если вы не можете указать частоту кадров? Это критично для многих веб приложений и особенно для игр.

Вот техника, которую вы можете использовать:

Javascript

var fps = 15;
function step() {
    setTimeout(function() {
        requestAnimationFrame(step);
        // Drawing code goes here
    }, 1000 / fps);
}

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

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

Javascript

var time;
function step() {
    requestAnimationFrame(step);
    var now = new Date().getTime(),
        dt = now - (time || now);

    time = now;

    // для примера сдвиг по оси х
    this.x += 10 * dt; // Увеличивать х на десять единиц в секунду
}

Удачи. Приятного “анимирования” :) :P

7 комментариев на “Что такое requestAnimationFrame?
  1. Владимир пишет:

    “Лучшим решением в случае анимации, бесспорно будет css transitions и animations. Но, к сожалению на момент написания статьи, ie9 (последняя версия ослика) ничего о css анимации не знает, да и в некоторых случаях (анимация на canvas например), css никак не поможет”

    http://caniuse.com/#search=requestAnimationFrame похоже и это решение не подходит для ИЕ9)

  2. Nik777 пишет:

    Очень толково, спасибо!

  3. Ateiri пишет:

    Владимир, судя по коду для IE9 он эмулирует данную функцию.

    if (!window.requestAnimationFrame){ 
      window.requestAnimationFrame = function(callback, element) { 
        var currTime = new Date().getTime(); 
        var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 
        var id = window.setTimeout(function() { 
          callback(currTime + timeToCall); 
        }, timeToCall); 
        lastTime = currTime + timeToCall; return id; 
      }; 
    }
    
  4. Mblkolo пишет:

    var fps = 15;
    function step() {
    setTimeout(function() {
    requestAnimationFrame(step);
    // Drawing code goes here
    }, 1000 / fps);
    }
    Я правильно понимаю, что вызывается функция step, которая вызывает таймаут, потом по таймауту системе сообщают, что при первой возможности вызови функцию step для “лучшей” отрисовки, а сами начинаем тут же в таймауте рисовать. Это же не правильно. По идее мы должны рисовать сразу в функции step, а таймаутом дать отсрочку вызова отрисовки слеющего шага. Типа так:

    var fps = 15;
    function step() {
    // Drawing code goes here

    setTimeout(function() {
    requestAnimationFrame(step);
    }, 1000 / fps);
    }

  5. Артем пишет:

    Здравствуйте!
    Не могу не где найти ответ на вопрос, как быть с анимацией css и скриптами js на мобильных ОС, где нет курсора.
    В частности у меня на сайте, реализовано всплывающее меню при наведении курсора на объект(картинка+текст=ссылка страницу).
    Подскажите пожалуйста, как реализовать?
    Заранее спасибо за ответ!

Оставить комментарий

Ваш email не будет опубликован. Обязательные поля отмечены *

*

* Copy This Password *

* Type Or Paste Password Here *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Узнавай о новых статьях первым!

Спасибо за лайк в FACEBOOK
Подписывайтесь на новости вконтакте
Последние статьи от html5.by