CSS
SVG

Управление синхронизацией для анимаций на основе сценариев ("requestAnimationFrame")

В Internet Explorer 10 и приложения Магазина Windows на JavaScript добавлена поддержка метода requestAnimationFrame, который предоставляет более органичный и эффективный способ создания анимированных веб-страниц, вызывая кадр анимации, когда система готова к его отрисовке. До этого API те анимации, при отрисовке которых использовались элементы setTimeout и setInterval, не предоставляли веб-разработчикам эффективных средств настройки графических таймеров анимаций. Это приводило к лишней отрисовке анимаций, лишним тактам центрального процессора и повышенному потреблению энергии. Кроме того, анимация часто отображается, даже когда веб-сайт невидим для пользователя, — в частности, когда открыты страницы на фоновых вкладках либо когда окно браузера свернуто.

Когда анимация использует разрешение таймера JavaScript 10 мс для отрисовки анимаций, результатом становится несоответствие синхронизации, как показано здесь.

Схема, показывающая разницу между частотой экрана и разрешением таймера JavaScript

Верхний ряд представляет интервал обновления экрана 16,7 мс, характерный для большинства мониторов, а нижний — стандартный метод setTimeout с интервалом 10 мс. Каждый третий кадр не выводится на экран (отмечен красной стрелкой), потому что запрос на отрисовку следующего кадра отправляется перед началом интервала обновления экрана для предыдущего кадра. Это приводит к прерывистому отображению анимации, так как теряется каждый третий кадр. Такое уменьшение разрешения таймера может также отрицательно сказаться на времени работы батареи и производительности других приложений.

Метод requestAnimationFrame (описанный в спецификации консорциума W3C Управление расписанием для анимаций на базе сценария) может решить проблему потерянного кадра, так как он позволяет уведомлять приложения о том, когда (и только когда) браузер должен обновить страницу. В результате приложения идеально синхронизируются с интервалом обновления браузера и используют только необходимое им количество ресурсов. Переключиться с setTimeout на requestAnimationFrame легко, так как оба они используют единый обратный вызов. Для непрерывной анимации снова вызовите requestAnimationFrame после вызова функции анимации.

Использование requestAnimationFrame

Для использования этого нового API следует просто вызвать requestAnimationFrame с помощью функции обратного вызова. Синхронизацией управляет система. Если в настоящее время вы используете setTimeout для обеспечения синхронизации анимации следующим образом:


var handle = setTimeout(renderLoop, PERIOD);

вы можете заменить метод setTimeout на requestAnimationFrame, как показано далее:


var handle = requestAnimationFrame(renderLoop);

Это обеспечит первое обновление. Чтобы анимация продолжала работать, вызовите requestAnimationFrame снова из функции обратного вызова (показано здесь как renderLoop).


<!DOCTYPE html>
<html>
  <head>
    <title>Script-based animation using requestAnimationFrame</title>
    <style type="text/css">
     #animated { 
       position: absolute; top:100px; 
       padding: 50px; background: crimson; color: white; 
       }
    </style>

 
  </head>
  <body>
    <div>Click the box to stop and start it</div>
    <div id="animated">Hello there.</div>
   
    <script type="text/javascript">
      // global variables 
      var elm = document.getElementById("animated");
      var handle = 0;
      var lPos = 0;

      renderLoop();  // call animation frame to start

      function renderLoop() {
          elm.style.left = ((lPos += 3) % 600) + "px";
          handle = window.requestAnimationFrame(renderLoop);
      }

      // click the box to stop the animation 
      document.getElementById("animated").addEventListener("click", function () {
        if (handle) {
          window.cancelAnimationFrame(handle);
          handle = null;
        } else {
          renderLoop();
        }
      }, false);

      </script>

  </body>
</html>


Этот пример начинается с сохранения элемента <div> в глобальной переменной с помощью getElementById. Функция renderLoop() вызывается для запуска анимации. После перемещения элемента <div> requestAnimationFrame снова вызывается для настройки следующего движения. Это будет продолжаться, пока вы не закроете браузер или не щелкнете элемент <div>.

Метод addEventListener обрабатывает событие щелчка элемента <div>. Когда вы щелкаете элемент, вызывается cancelAnimationFrame с текущим дескриптором.

Время в функции обратного вызова

Функцию обратного вызова можно использовать практически для любой анимации, например SVG, Canvas или свойств CSS, как показано здесь. Функция обратного вызова имеет один входящий параметр time, который представляет время вызова для функции обратного вызова. Это DOMHighResTimeStamp, очень точное время, измеряемое с начала навигации по странице. DOMHighResTimeStamp измеряется в миллисекундах с точностью до тысячной доли миллисекунды. Это значение времени нельзя непосредственно сравнить с результатом функции Date.now(), который измеряет время в миллисекундах с 1 января 1970 года. Если вы хотите сравнить параметр time с текущим временем, используйте window.performance.now для получения текущего времени. Изменение значения времени с DOMTimeStamp на DOMHighResTimeStamp является более поздним изменением последней версии черновика спецификации консорциума W3C Управление расписанием для анимации на базе сценария, и некоторые поставщики могут все еще реализовывать его как DOMTimeStamp. В более ранних версиях спецификации консорциума W3C использовался параметр DOMTimeStamp, который позволял использовать Date.now для получения текущего времени.

Поддержка браузеров

Как упоминалось ранее, некоторые поставщики браузеров могут до сих пор использовать параметр DOMTimeStamp или не реализовать функцию расписания window.performance.now. Так как версия спецификации консорциума W3C еще не является окончательной, производители других браузеров создали собственную реализацию requestAnimationFrame и cancelAnimationFrame с префиксами. Этот пример предоставляет версию программы анимации, которая работает в нескольких браузерах и использует время для перемещения элемента <div>.


<!DOCTYPE html>
<html>
<head>
<title>Script-based animation using requestAnimationFrame</title>
<style type="text/css">
div { position: absolute; left: 10px; top:100px; padding: 50px;
  background: crimson; color: white; }
</style>
<script type="text/javascript">
    var requestId = 0;
    var startime = 0;
    var lpos = 0;
    var elm;

    function init() {
        elm = document.getElementById("animated");
    }
    
    function render() {
        elm.style.left = ((lpos += 3) % 600) + "px";
        requestId = window.requestAFrame(render);
    }

    function start() {
        if (window.performance.now) {
            startime = window.performance.now();
        } else {
            startime = Date.now();
        }
        requestId = window.requestAFrame(render);
    }
    function stop() {
        if (requestId)
            window.cancelAFrame(requestId);        
    }


    // handle multiple browsers for requestAnimationFrame()
    window.requestAFrame = (function () {
        return window.requestAnimationFrame ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                window.oRequestAnimationFrame ||
                // if all else fails, use setTimeout
                function (callback) {
                    return window.setTimeout(callback, 1000 / 60); // shoot for 60 fps
                };
    })();

    // handle multiple browsers for cancelAnimationFrame()
    window.cancelAFrame = (function () {
        return window.cancelAnimationFrame ||
                window.webkitCancelAnimationFrame ||
                window.mozCancelAnimationFrame ||
                window.oCancelAnimationFrame ||
                function (id) {
                    window.clearTimeout(id);
                };
    })();

   
</script>
</head>
<body onload="init();">

<div id="animated">Hello there.</div>
<button onclick="start()">Start</button>
<button onclick="stop()">Stop</button>
</body>
</html>


Этот пример является расширенным вариантом примера из спецификации консорциума W3C, в который добавлены элементы с префиксами поставщиков. Этот пример дополнительно проверяет наличие поддержки window.performance.now и, в случае ее отсутствия, использует Date.now для получения текущего времени.

Справочник по API

requestAnimationFrame

Примеры и учебники

Пример эффективных приемов анимации с использованием requestAnimationFrame

Демонстрационные версии тестовых выпусков Internet Explorer

Bubbles (Мыльные пузыри)
API requestAnimationFrame

Записи блога IEBlog

Стандарт Web Performance консорциума W3C — продолжение инвестиций в производительность
API-интерфейсы для измерения производительности веб-приложений быстро получили статус Candidate Recommendation консорциума W3C
Развитие стабильного Интернета в Internet Explorer 10 Release Preview
Отчитываемся за год: рабочая группа консорциума W3C по производительности веб-приложений
Эффективное использование оборудования компьютера в HTML5: новые API для повышения производительности веб-сайтов, часть 1

Спецификация

Управление синхронизацией для анимаций на основе сценариев

 

 

Показ:
© 2014 Microsoft