Controle de tempo para animações com base em scripts ("requestAnimationFrame")

O Internet Explorer 10 e os aplicativos da Windows Store em JavaScript permitem o método requestAnimationFrame, que é uma maneira mais suave e eficiente de criar páginas da Web animadas chamando o quadro de animação quando o sistema está pronto para pintar o quadro. Antes dessa API, as animações desenhadas com setTimeout e setInterval não forneciam um meio eficiente para os desenvolvedores da Web programarem temporizadores de elementos gráficos para animações. Isso resultava em animações exageradas, ciclos de CPU desperdiçados e consumo excessivo de energia. Além disso, as animações geralmente ocorrem mesmo quando um site não está visível, especialmente quando o site usa páginas em guias de segundo plano ou quando o navegador é minimizado.

Quando uma animação usa uma resolução do temporizador do JavaScript de 10 ms para desenhar animações, você obtém uma discrepância de temporização, conforme mostrado aqui.

Diagrama mostrando a diferença de frequência entre uma frequência de exibição e uma resolução do temporizador do JavaScript

A linha superior representa a frequência de exibição de 16,7 ms exibida na maioria dos monitores e a linha inferior representa um setTimeout típico de 10 ms. Cada terceiro desenho não pode ser pintado (indicado pela seta vermelha) porque ocorre outra solicitação de desenho antes do intervalo de atualização do monitor. Esse excesso de desenho resulta em animações cortadas, porque todo terceiro quadro é perdido. Esta redução de resolução do timer também pode afetar negativamente a vida útil da bateria e reduzir o desempenho de outros aplicativos.

O método requestAnimationFrame (definido na especificação Controle de tempo para animações baseadas em script do W3C (World Wide Web Consortium) pode resolver os problemas de quadro perdido porque permite que os aplicativos sejam notificados quando (e somente quando) o navegador precisar atualizar a exibição da página. Como resultado, os aplicativos são perfeitamente alinhados com o intervalo de pintura do navegador e usam apenas a quantidade apropriada de recursos. A mudança de setTimeout para requestAnimationFrame é fácil, pois ambos agendam um retorno de chamada único. Para animação contínua, chame requestAnimationFrame novamente depois que a função de animação for chamada.

Usando requestAnimationFrame

Para usar essa nova API, basta chamar requestAnimationFrame usando uma função de retorno de chamada. O tempo é manipulado pelo sistema. Se você estiver usando setTimeout para conduzir o seu calendário de animação, como este:


var handle = setTimeout(renderLoop, PERIOD);

Você pode substituir setTimeout por requestAnimationFrame, como mostrado aqui:


var handle = requestAnimationFrame(renderLoop);

Isso cuida da sua primeira repintura. Para que a animação continue, chame requestAnimationFrame novamente a partir de sua função de retorno de chamada (mostrada aqui como 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>


Este exemplo começa salvando o elemento <div> em uma variável global usando getElementById. A função renderLoop() é chamada para iniciar a animação. Após o elemento <div> ser reposicionado, requestAnimationFrame é chamado novamente para configurar o próximo movimento. Isso continua até que você feche o navegador ou clique no elemento <div>.

O método addEventListener manipula o evento de clique no elemento <div>. Quando você clica no elemento, cancelAnimationFrame é chamado com o identificador atual.

Tempo em uma função de retorno de chamada

Você pode usar a função de retorno de chamada para animar praticamente qualquer coisa, por exemplo, SVG, Canvas ou, como mostramos aqui, propriedades CSS. A função de retorno de chamada tem um parâmetro time de entrada, que representa a hora em que essa função é chamada. Este é um DOMHighResTimeStamp, um tempo de alta resolução medido desde o início da navegação da página. DOMHighResTimeStamp é medido em milissegundos de precisão de um milésimo de um milissegundo. Esse valor de tempo não é diretamente comparável com Date.now(), que mede o tempo em milissegundos desde 1 de janeiro de 1970. Se você quiser comparar o parâmetro time com a hora atual, use window.performance.now para a hora atual. A alteração do valor de tempo de DOMTimeStamp para DOMHighResTimeStamp foi uma alteração final do projeto no último Rascunho dos Editores da especificação Controle de tempo W3C para animações baseadas em scripts, e alguns fornecedores ainda podem implementá-la como DOMTimeStamp. As versões anteriores da especificação W3C usou o DOMTimeStamp, que permitiu usar o Date.now para a hora atual.

Suporte do navegador

Como mencionado anteriormente, alguns fornecedores de navegadores ainda podem implementar o parâmetro DOMTimeStamp ou não implementaram a função de temporização window.performance.now. Como a especificação W3C ainda não é final, outros fabricantes de navegador criaram suas próprias implementações prefixadas de requestAnimationFrame e cancelAnimationFrame. Este exemplo fornece uma versão do nosso programa de animação que funciona em diversos navegadores e usa o tempo para mover o elemento <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>


Este exemplo é uma extensão de uma amostra de especificação W3C, com elementos prefixados do fornecedor adicionados. O exemplo também testa para o window.performance.now e, se ele não tiver suporte, usa Date.now para obter a hora atual.

Referência de API

requestAnimationFrame

Amostras e tutoriais

Animações eficientes usando a amostra requestAnimationFrame

Demonstrações do Test Drive do Internet Explorer

Bolhas
API requestAnimationFrame

Postagens no blog do IE

Desempenho da Web do W3C: continuando os investimentos em desempenho
APIs de desempenho da Web rapidamente se tornam recomendações candidatas do W3C
Levando a Web estável adiante no IE10 Release Preview
O ano em revisão: grupo de trabalho de desempenho da Web do W3C
Usando o hardware do computador de forma mais eficiente no HTML5: novas APIs de desempenho da Web, parte 1

Especificação

Controle de tempo para animações com base em scripts

 

 

Mostrar:
© 2014 Microsoft