フロントエンドのパフォーマンスチューニングで一番厄介なのは、「なぜか画面がカクつく」というフワッとした現象に直面したときだ。JavaScriptの実行時間は問題ない、メモリリークもしてない。なのに、スクロールするたびに微妙に引っかかる。
大抵の場合、犯人は「無駄な再描画(ペイント)」だ。
ブラウザのレンダリングエンジンが裏側で何をやっているのか、その泥臭いメカニズムを理解していないと、私たちは知らず知らずのうちにGPUに対して無駄な重労働を課し、ユーザーのスマホのバッテリーを溶かし続けることになる。
今日は、ブラウザの開発者ツールを武器にして、この「ペイントフラッシング(Paint Flashing)」を可視化し、無駄な再描画の芽をスッキリと摘み取るための実践的なアプローチを伝授しよう。
—
そもそも、ブラウザは裏側で何をやっているのか?
DOMツリーとCSSOMツリーが合体してレンダーツリー(Layout Tree)ができあがり、各要素のサイズや位置が決まる――ここまでは基本の「き」だ。
問題はその先にある。レイアウト(リフロー)が終わった後、ブラウザは実際にピクセルを画面に描き出す「ペイント(Paint)」という工程に入る。
そして近代的なブラウザは、パフォーマンスを極限まで引き上げるために、画面をいくつかの「レイヤー(Layer)」に分割して管理している。これをレイヤー合成(Compositing)と呼ぶ。
ここでシニアとして君に強く覚えておいてほしい鉄則がある。
> 「DOMを触ればレイアウトが起き、スタイルを変えればペイントが起き、プロパティによってはレイヤーの再構築(Rasterization)が走る」
特に、頻繁に変わる要素が不必要にメインのペイントレイヤーに巻き込まれていると、ほんの小さなアニメーションであっても、画面全体の広範囲な領域が「真っ白になって塗り直される」という無駄な処理(ペイントフラッシング)が発生する。これがカクつきの正体だ。
—
開発者ツールで「ペイントの瞬間」を暴く
ブラウザに「今、画面のどこを塗り直しているのか」を白日の下に晒させよう。Google ChromeのDevToolsには、そのための強力な機能が隠されている。
ペイントフラッシング(Paint Flashing)の有効化手順
1. Chromeで対象のWebページを開き、`F12`(Macなら`Cmd + Option + I`)でDevToolsを開く。
2. コマンドメニューを召喚する(`Ctrl + Shift + P` または `Cmd + Shift + P`)。
3. 「`Show paint flashing`」と入力し、「Rendering: Show paint flashing」を選択して実行する。
これで準備は完了だ。ページ内でマウスを動かしたり、スクロールしたり、ボタンをクリックしたりしてみてほしい。
画面の再描画(Paint)が発生した領域が、緑色の閃光のようにチカチカとハイライトされるはずだ。
もし、ボタンを1つホバーしただけなのに、画面の大部分が緑色に発光していたら……おめでとう、そこには「無駄なペイントの嵐」が吹き荒れている。今すぐコードを修正する必要がある。
—
実践:無駄なペイントを生むアンチパターンと修正コード
百聞は一見にしかず。現場でよくやりがちな「やってはいけない実装」と、それを美しく解決するコードを見ていこう。
以下のサンプルは、「スクロールするたびに固定ヘッダーの下で何かが再計算され、ページ全体がペイントされてしまう」という悪夢のような状況を模したシミュレーションだ。
🚨 アンチパターン:不必要なレイヤー巻き込みと重い再描画
辛口フロントエンド道場
下のボックスにマウスを乗せてみてください。Paint Flashingを入れていると、ヘッダーや画面全体が緑色に光り狂うのがわかります。
このコードの何がダメかというと、`.dynamic-box` の `box-shadow` を変化させている点だ。影の描画はコストが高い。しかも、これによって周辺のレイアウトに影響が出ると判断されると、ブラウザは余計な領域までペイントし直す。
✨ ベストプラクティス:ハードウェアアクセラレーションとレイヤーの分離
では、どう直すべきか?
基本方針は2つ。
1. ペイントコストの高いプロパティ(`box-shadow` など)の多用を避け、GPUで処理できるプロパティ(`transform`, `opacity`)に置き換える。
2. 頻繁に変化する要素を「独立したレイヤー(Compositing Layer)」に昇格させ、他の領域のペイントを巻き込まないようにする。
先ほどのコードを、プロの手による洗練された実装に書き換えてみよう。
辛口フロントエンド道場(最適化済)
ボックスにマウスを乗せてみてください。Paint Flashingが最小限のエリア(ボックスの領域のみ)に限定されているのが確認できます。
—
シニアからの実務アドバイス:過剰最適化への罠
ここまで「ペイントを減らせ」「レイヤーを分けろ」と熱く語ってきたが、最後にプロとして重要な注意点を一つ。
「`will-change` や `translateZ(0)` をすべての要素にベタ貼りしてはならない」
これをやりすぎると、ブラウザは数え切ほどのレイヤーをGPU上に生成しようとし、今度はVRAM(ビデオメモリ)の枯渇という別の地獄を引き起こす。結果的に、メモリ不足でスクロールがカクついたり、モバイル端末でブラウザがクラッシュしたりする本末転倒な事態に陥る。
薬も過ぎれば毒となる。
ペイントフラッシングツールを使って「本当に重い箇所」「ユーザーの操作感に直結するアニメーション要素」をピンポイントで特定し、そこだけに的確にメスを入れる。これができるエンジニアこそが、現場で頼られる真のフロントエンド・アーキテクトだ。
さあ、今書いている君のプロジェクトのコードでも、さっそく `Show paint flashing` を試してみてくれ。思わぬ場所が緑色に発光して、冷汗をかくかもしれないが、それこそが成長の第一歩だ。

コメント