Webアプリケーションのある処理によって画面の更新頻度が多く、CPUが弱いマシンだとフリーズしてしまう問題が起きた
これを解決するために、デバウンスを実行しようと考えた時、遅延方法として requestAnimationFrame がいいのではないかと聞いた
requestAnimationFrame をよく知らなかったので調べた
requestAnimationFrame とは?
window.requestAnimationFrame() は、ブラウザのレンダリングサイクルに合わせてアニメーションを実行するためのJavaScriptメソッド
一般的に次のように使用する
javascriptfunction animate(timestamp) { // アニメーション処理 // timestampはコールバック実行時の時間(ミリ秒) // 次のフレームをリクエスト requestAnimationFrame(animate); } // アニメーション開始 requestAnimationFrame(animate);
requestAnimationFrame の主な特徴
1. ブラウザの描画サイクルに最適化
requestAnimationFrame はブラウザの描画タイミングに合わせて実行されるため、フレームの境界で処理が行われ、画面の更新がスムーズになる
通常のディスプレイでは60FPS(16.7ミリ秒ごと)に合わせて実行されるが、高リフレッシュレートのディスプレイにも自動的に対応している
2. バッテリー効率の向上
タブがバックグラウンドになった場合や画面外にスクロールされた場合、requestAnimationFrame の実行は自動的に抑制される
これにより無駄な処理を減らし、特にモバイルデバイスでのバッテリー寿命を延ばせる
3. 正確なタイムスタンプの提供
コールバック関数には高精度のタイムスタンプが渡され、これを利用して時間ベースのアニメーションを実装できる
デバイスの処理速度に関わらず一定の速度でアニメーションを実行するのに役立つ
従来の方法を使用する場合の問題点
setTimeout や setInterval などの従来のタイマー関数を使ったアニメーション実装には、以下のような問題がある
1. パフォーマンスの低下
- 画面更新との非同期: ブラウザのレンダリングサイクルと同期せず、フレームの途中で更新が実行されることがある
- ティアリング: 画面の一部だけが更新された状態で表示される「画面の裂け」が発生
- フレームスキップ: 処理が間に合わずフレームが飛ぶことによる不自然な動き
2. 電力効率の悪化
- バックグラウンドタブや非表示エリアでも処理が継続する
- モバイルデバイスでのバッテリー消費が増加
- リソースの無駄遣いになる
3. タイミングの不正確さ
// 悪い例: setInterval を使ったアニメーション setInterval(() => { position += 5; // 固定量の移動 box.style.transform = `translateX(${position}px)`; }, 16.7); // 約60FPSを意図
上記のようなコードでは:
- 実際の間隔は不正確(最小間隔の保証のみ)
- 処理負荷が高い場合に遅延が蓄積
- デバイス性能によってアニメーション速度が変わる
実践的な使用例: 時間ベースのアニメーション
requestAnimationFrame と経過時間を使った正しいアニメーション実装例:
let boxElement = document.querySelector('.box'); let start = null; function moveBox(timestamp) { if (!start) start = timestamp; // 経過時間を計算 const elapsed = timestamp - start; // 経過時間に基づいて位置を計算(5秒で300px移動) const position = Math.min(elapsed / 5000 * 300, 300); // 要素のスタイルを更新 boxElement.style.transform = `translateX(${position}px)`; // アニメーションが終了していなければ続行 if (position < 300) { requestAnimationFrame(moveBox); } } // アニメーション開始 requestAnimationFrame(moveBox);
この方法では、どのデバイスでも同じ期間(5秒)で動作が完了する