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;
}
}());