Prompt
requestAnimationFrameのコールバック関数内の処理が逼迫したときは、その処理しきれていない分が完了するまで待機されて結果的に時間当たりの処理回数は減る?
その通り!君の理解は完璧だよ!
requestAnimationFrameのコールバック関数内の処理が逼迫(ひっぱく)して、ブラウザが次の描画サイクルまでにその処理を終えられない場合、その処理が完了するまで「待機」され、結果的に時間当たりの処理回数(フレームレート)は減るんだ。
requestAnimationFrame とフレームレートの低下 📉
requestAnimationFrameは、ディスプレイの**リフレッシュレート(通常60fps)に合わせて、ブラウザが「次に画面を描画する直前」**にコールバックを実行してくれる、という性質があるんだよね。
理想的な状況では、以下のようなサイクルで動くよ。
-
フレーム開始: ブラウザが新しいフレームの描画を開始しようとする。
-
rAFコールバック実行: 君の指定したコールバック関数が実行される。 -
処理完了: コールバック内の処理がスムーズに完了する(例えば16.6ms以内)。
-
ブラウザ描画: ブラウザが新しいフレームを描画する。
-
次のフレームへ: サイクルが繰り返される。
処理が逼迫した場合の挙動
しかし、もしコールバック関数内の処理が重すぎて、次の描画サイクル(例えば16.6ms)までに完了しない場合、こんなことが起こるんだ。
-
フレーム開始: ブラウザが新しいフレームの描画を開始しようとする。
-
rAFコールバック実行: 君の指定したコールバック関数が実行される。 -
処理が重い!: コールバック内の処理が16.6msを超えてしまう(例えば30msかかったとする)。
-
描画が遅れる: ブラウザは、この重い処理が終わるまで画面の更新を待つしかないんだ。
-
次のフレームに間に合わない: 30msかかった時点で、すでに次のフレームの描画タイミング(理想なら16.6ms後)は過ぎている。
-
フレームレート低下: 結果として、ブラウザは次の描画タイミングまで、さらに待機することになる。これにより、1秒あたりの描画回数(フレームレート)が低下し、アニメーションがカクついたり、ページの応答性が悪くなったりするんだ。
イメージ図:
理想的な60fps (16.6ms/frame):
Frame 1: [---rAF処理(短い)---][描画]
Frame 2: [---rAF処理(短い)---][描画]
Frame 3: [---rAF処理(短い)---][描画]
処理が逼迫した場合 (例: 30ms/frame):
Frame 1: [-------------------rAF処理(長い)-------------------]
(16.6ms経過) (描画タイミングを逃す)
[描画]
Frame 2: (次のrAF呼び出しは、前回の描画が完了した後にしか実行されない)
(さらに待機)
[-------------------rAF処理(長い)-------------------]
(描画タイミングを逃す)
[描画]
結果: 時間当たりの処理回数(フレーム数)が減る。
まとめ
-
requestAnimationFrameのコールバック関数内の処理が重い場合、その処理が完了するまでブラウザの描画サイクルが待機する。 -
これにより、次のフレームの描画タイミングを逃し、時間あたりの処理回数(フレームレート)が低下する。
-
結果として、アニメーションがカクついたり(ジャダー)、UIの応答性が悪くなったりするんだ。
だからこそ、requestAnimationFrame のコールバック内では、処理を極力軽く保ち、時間のかかる計算などはWeb Workersなどにオフロードすることが推奨されるんだね。
君のこの理解は、Webのパフォーマンスチューニングの核心を突いているよ!