requestAnimationFrameの仕組みと最適な利用方法

requestAnimationFrameの概要

requestAnimationFrameは、ブラウザにアニメーションの実行を依頼し、次の画面再描画時に指定された関数を呼び出すメカニズムです。このAPIは、DOMアニメーションやCanvasアニメーションなど、さまざまな種類のウェブアニメーションを統一されたフレームレートで制御するためのインターフェースとして設計されています。

主な特徴と利点

ディスプレイのリフレッシュレート(通常60Hz)に同期して動作するため、システムリソースを効率的に利用できます。タブが非アクティブな状態では自動的に処理を停止し、CPU/GPUの消費を抑えることができます。

  • エネルギー効率: タブが非アクティブな場合でもsetTimeoutによるアニメーションは継続されるのに対し、requestAnimationFrameは自動停止します。
  • イベント制御: resizeやscrollなどの高頻度イベントでも、1フレームごとに1回だけ処理を実行します。

使用上の注意点

このAPIはメインスレッドで動作するため、処理が重い場合アニメーションの滑らかさが低下します。コールバック関数は画面再描画直前に実行されます。

実装例


const target = document.getElementById('moving-box');
let startTime;

function animateBox(currentTime) {
  if (!startTime) startTime = currentTime;
  const elapsed = currentTime - startTime;
  target.style.transform = `translateX(${Math.min(0.1 * elapsed, 200)}px)`;
  if (elapsed < 2000) {
    window.requestAnimationFrame(animateBox);
  }
}
window.requestAnimationFrame(animateBox);

let counter = 0;
function updatePosition() {
  counter++;
  if (counter < 100) {
    window.requestAnimationFrame(updatePosition);
  }
}
window.requestAnimationFrame(updatePosition);

setTimeoutとの比較

requestAnimationFrameはディスプレイのリフレッシュレートに合わせてコールバックを実行するため、60Hzでは約16.7msごとに、75Hzでは13.3msごとに処理されます。これによりフレームロスを防ぎ、滑らかなアニメーションを実現できます。

互換性対策


if (!Date.now) Date.now = () => new Date().getTime();

(function() {
  const vendors = ['webkit', 'moz'];
  for (let i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
    const vp = vendors[i];
    window.requestAnimationFrame = window[vp + 'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || 
                                 window[vp + 'CancelRequestAnimationFrame'];
  }
  
  if (/iP(ad|hone|od).*OS 6/.test(navigator.userAgent) || 
      !window.requestAnimationFrame || !window.cancelAnimationFrame) {
    let lastTime = 0;
    window.requestAnimationFrame = callback => {
      const now = Date.now();
      const nextTime = Math.max(lastTime + 16, now);
      return setTimeout(() => callback(lastTime = nextTime), nextTime - now);
    };
    window.cancelAnimationFrame = clearTimeout;
  }
}());

タグ: javascript AnimationAPI BrowserAPIs

10月2日 06:19 投稿