ブラウザの「レンダリング・コスト」を制する:リフロー・リペイントを支配する技術
やあ。現場でコードを書いていると、「なぜか画面がカクつく」「複雑なアニメーションを動かすとCPU使用率が跳ね上がる」という壁にぶつかることはないだろうか。
フロントエンドのパフォーマンスチューニングにおいて、「なんとなくJSを減らす」「なんとなく画像を圧縮する」といった対策はもう卒業しよう。ブラウザが裏側で何をしているのか、その「物理的な制約」を理解すれば、コードの書き方は劇的に変わる。
今日は、レンダリングの心臓部であるReflow(再レイアウト)とRepaint(再描画)、そしてGPUアクセラレーションについて、現場の知見を交えて深掘りしていくぞ。
—
ブラウザが「泣く」瞬間:ReflowとRepaintの正体
ブラウザのレンダリングパイプラインは、大きく分けて以下の5つのステップで動いている。
1. JavaScript(JSでDOMをいじる)
2. Style(CSSを計算する)
3. Layout(Reflow:要素の幾何学的な位置やサイズを計算する)
4. Paint(Repaint:色、影、画像などをピクセルとして塗りつぶす)
5. Composite(ブラウザの層(レイヤー)を重ねて画面を合成する)
ここでの鉄則は、「後ろの工程に行けば行くほどコストが重い」ということだ。
- Reflow(Layout)を伴う変更: 要素の幅や高さ、フォントサイズなどを変えるとき。ブラウザはページ全体の幾何学を再計算する必要がある。これはブラウザにとって一番の重労働だ。
- Repaintを伴う変更: 色や背景色、ボーダーの変更。レイアウトには影響しないが、ピクセルを描き直す必要がある。これも地味にコストがかかる。
- Compositeのみ: `transform` や `opacity`。これらはGPUに処理を丸投げできるため、メインスレッドを止めずに滑らかに動かせる。
—
実践:高コストを避け、GPUを味方につける
フロントエンドエンジニアとして、我々が目指すべきは「Compositeのみ」で完結するアニメーションの構築だ。
以下のコードを見てほしい。左側が「絶対に避けるべきアンチパターン」、右側が「パフォーマンスを意識したモダンな書き方」だ。
/
- アンチパターン:Layoutを発生させる
- widthやtop/leftを変えると、毎回ブラウザは再レイアウトを計算し、
- メインスレッドを占有してしまう。
/
const box = document.querySelector(‘.box’);
box.style.width = ‘200px’; // 毎フレームのリフロー発生源!
box.style.top = ‘100px’; // これもリフローを引き起こす
/
- 推奨パターン:Compositeのみで完結させる
- transformとopacityは、ブラウザが別レイヤーで処理するため、
- メインスレッドへの負荷が極めて低い。
/
const box = document.querySelector(‘.box’);
// 要素の移動には transform: translate を使うのが鉄則
box.style.transform = ‘translate(100px, 100px)’;
// 透明度の変更には opacity を使う
box.style.opacity = ‘0.5’;
—
なぜ「transform」は速いのか?
ここで一つ、君たちに覚えておいてほしいブラウザの裏側の知見がある。
`transform` や `opacity` を使うと、ブラウザはその要素を「独立したレイヤー(Compositing Layer)」として扱うんだ。
例えるなら、巨大なキャンバスに絵を描き直すのではなく、「透明なシートに絵を描いて、それを元の絵の上に重ねる」ようなイメージだ。背景の絵を描き直す必要がないから、GPUの力を使って爆速で合成できる。
ただし、注意点もある。レイヤーを無闇に増やしすぎると、今度はメモリを食いつぶす。 すべての要素を `will-change: transform` でレイヤー化すればいいというわけではない。必要な箇所にだけ、ピンポイントで使うのがプロの仕事だ。
—
今すぐ現場で使える「コスト削減チェックリスト」
明日からの実装で、以下のリストを意識してみてくれ。
- 幅・高さの変更(width/height)は極力避ける: アニメーションさせるなら `transform: scale()` を使う。
- 位置の移動は top/left ではなく transform: これだけでカクつきは劇的に減る。
- DOMの読み書きを交互に行わない: `offsetHeight` などを取得した直後に `style` を変えると、ブラウザは強制的にリフローを実行する(強制同期レイアウトという。これは敵だ)。読み込み(Read)を先に、書き込み(Write)を後にまとめて行うのが鉄則だ。
- DevToolsを使い倒す: Chrome DevToolsの「Rendering」タブにある「Paint Flashing」をオンにしてごらん。アニメーション中に画面が緑色に光る場所があれば、そこがRepaintされている場所だ。光りすぎている箇所は、最適化のチャンスだぞ。
最後に
ブラウザは優秀だが、魔法使いではない。我々エンジニアが「ブラウザにどう計算させるか」という意識を持つだけで、エンドユーザーが見る画面の滑らかさは段違いになる。
理論を知った上で、現場の泥臭い仕様と戦う。それができるのが、真のフロントエンド・スペシャリストだ。次に画面がカクついた時、ブラウザが裏で泣いていないか、一度想像してみてほしい。
それじゃ、また現場で会おう。健闘を祈る。

コメント