ブラウザの「心臓の鼓動」に合わせる:requestAnimationFrameが最強である理由
フロントエンドの現場で「画面がカクつく」「スクロールが重い」というバグに直面したとき、君たちはどうする?多くのエンジニアが真っ先に疑うのはJavaScriptの実行速度だ。しかし、往々にして犯人はそこじゃない。真犯人は、「ブラウザが描画しようとしているタイミングと、お前がDOMを書き換えようとするタイミングのミスマッチ」にある。
今日は、ブラウザレンダリングの深淵に触れつつ、`requestAnimationFrame` (rAF) を使った「正しい描画の作法」について、現場の知見を叩き込む。
—
ブラウザの描画パイプラインを理解する
まず、ブラウザが画面を更新する仕組みを正しく捉えておこう。ブラウザは基本的に、ディスプレイの更新頻度(一般的に60Hz = 16.6ms周期)に合わせて、以下のパイプラインを回している。
1. JavaScript: スクリプトを実行し、DOMを操作する。
2. Style: CSS計算(Recalculate Style)を行う。
3. Layout (Reflow): 要素のサイズや位置を計算する。
4. Paint: ピクセルを塗りつぶす。
5. Composite: 重ね合わせる(GPUの出番)。
問題は、`setTimeout` や `setInterval` でDOMを触ったときだ。これらはブラウザの描画サイクルを一切考慮しない。「今すぐ実行しろ」という命令は、運が悪ければ「ブラウザがまさに次のフレームの準備を始めた直後」に割り込むことになる。するとどうなるか? ブラウザは無理やり次のフレームで修正を反映させるために、余計なリフローやリペイントを強いられる。
これが「カクつき」の正体であり、ブラウザに無駄な計算を強いる「非効率な開発」の典型だ。
—
requestAnimationFrame:ブラウザとの「握手」
`requestAnimationFrame` は、いわばブラウザからの「次のフレームを描画する準備ができたよ!」という合図を待つためのAPIだ。これを使うと、ブラウザは「描画直前に実行すべきタスク」として君のコードをキューイングしてくれる。
実践:滑らかなアニメーションを実装する
例えば、要素を滑らかに移動させるコードを書くとき、`setInterval` を使ってはいけない。以下のように書くのがプロの流儀だ。
/
- 要素を滑らかに動かすためのメインループ
- @param {HTMLElement} element – 操作するDOM要素
/
function animate(element) {
let start = null;
function step(timestamp) {
if (!start) start = timestamp;
const progress = timestamp – start;
// 1. DOMの読み込み(ここで読み込みと書き込みを分けるのがコツ)
// 2. DOMの更新
// transformを使うことでLayoutやPaintをスキップし、Compositeだけで処理させる
element.style.transform = `translateX(${Math.min(progress / 10, 200)}px)`;
// まだ終わっていないなら次のフレームを要求
if (progress < 2000) {
requestAnimationFrame(step);
}
}
// 初回呼び出し
requestAnimationFrame(step);
}
---
現場で陥る「強制リフロー」の罠
ここで一つ、シニアレベルの忠告をしておく。`requestAnimationFrame` を使っていても、書き方を間違えると台無しになる。それが「強制リフロー(Forced Synchronous Layout)」だ。
例えば、`rAF` の中で以下のようなコードを書いたとする。
requestAnimationFrame(() => {
// 1. スタイルを変更する(書き込み)
element.style.width = ‘100px’;
// 2. 高さを取得する(読み込み)
// ここで問題発生!ブラウザは最新の値を計算するために、
// 実行中のJavaScriptを止めてLayoutを強制的に発生させる
console.log(element.offsetHeight);
});
`style` を書き換えた直後に `offsetHeight` を参照すると、ブラウザは「ああ、今の状態じゃ正しい高さがわからないから、今すぐ計算しなきゃ!」と、本来なら次のフレームでまとめてやるべき計算をその場で実行する。これが重い。
ベストプラクティス:
- 読み込みは読み込み、書き込みは書き込みでまとめる。
- 可能な限り、スタイルの変更は一箇所に集約し、ブラウザの「バッチ処理」に任せる。
—
まとめ:なぜこれを意識するのか
「コードが動けばいい」という段階を卒業したなら、次は「ブラウザのリソースをいかに効率的に使うか」に目を向けるべきだ。
`requestAnimationFrame` を使うことは、単にアニメーションを滑らかにするためだけではない。ブラウザの描画エンジンという「巨大なエンジン」と呼吸を合わせるということだ。この感覚を身につければ、複雑なUIを構築しても、ユーザーにストレスを与えるような重いWebサイトを作ることはなくなるはずだ。
明日からの開発で、`setTimeout` を見かけたら、一度立ち止まって考えてみてほしい。「これは本当にこのタイミングで動かす必要があるのか? ブラウザの描画サイクルと同期できないか?」と。
それが、君が真のフロントエンド・スペシャリストへと進化するための第一歩だ。健闘を祈る。

コメント