ピクセルパイプラインのCSSレンダリングパフォーマンスを考える -『CSS』

table of contents

    ピクセルが Web ページに描画されるパイプラインには 5 つの主要な領域があります。

    まず、スクリプティング JavaScript の処理です。スクリプトの処理中はメインスレッドが占有され、ほかの処理は行われません。なので、重い処理が発生すれば長くメインスレッドを占有してしまい、UIの動きがスムーズでなくなります。

    その後 CSS の計算が始まり、その流れは次のようになります。

    1. Calculate style
    2. Layout
    3. Paint
    4. Composite

    CSS のレンダリングステップ

    Webページのビジュアルを決めるスタイルは、レンダリング処理の速度に直結します。各ステップを説明しつつ、改善テクニックを見ていきます。

    Calculate Style

    CSSで宣言されたスタイル情報を、どの要素に一致するかをセレクタに基づいて選択適用します。これで各要素が、どのようなビジュアル情報を持っているのかが決定します。

    Layout

    レイアウトは、要素がスクリーン上でどう配置されるかの位置関係を計算します。たとえば、親の幅はその子の幅に影響を与え、ツリーの上から下絵と影響を与えます。なので CSSのレイアウトプロパティ(width, heightなど)を変更すると、ブラウザは他のすべての要素を確認してページをリフローする必要があります。影響を受けるエリアを再描画してそれらを合成します。

    Paint

    ペイントは、要素のすべての視覚的情報(color, borderなど)をピクセルに塗る描画処理をします。描画は一般的に、レイヤーと呼ばれる複数の面で行われます。CSSのペイントプロパティを変更すると、ページのレイアウトには影響しないのでレイアウトステップをスキップし、ペイントのみが行われます。

    ペイントはピクセルパイプラインのなかで、もっとも負荷がかかります。

    Composite

    コンポジットは、ブラウザが正しい順序でレイヤを描画するためのステップです。要素は互いに重なり合う可能性があるため、意図した順序で表示するために重要です。

    レイアウトもペイントも必要としないCSSのプロパティを変更した場合、ブラウザはコンポジットを行うだけで済みます。これはもっとも低コストで、アニメーションやスクロールといった、ライフサイクルの中で負荷の高いときに変化ても高負荷になりにくいです。

    CSS プロパティがどのステップに当てはまるのか

    CSS のどのプロパティがレイアウト・ペイント・コンポジットに影響を与えるかについては、 CSS Triggers に一覧があります。

    パフォーマンスをよくするための tips

    このパイプライン上でパフォーマンスを高めることができる CSS の書き方 tips のまとめです。

    子孫セレクタは高負荷になる可能性がある

    子孫セレクタを特定的な指定なしで使用すると、コストになる場合があります。その関係は親子に限定されないため、ブラウザはすべての子孫要素の一致を確認するからです。

    bad

    #nav li {}
    

    good

    #nav > li {}
    

    セレクタは右から左に評価される

    ブラウザはCSSを解析するとき、CSSのセレクタを右から左に解決します。

    _bad

    #nav ul li a {}
    

    すべての要素から a をみて、見つけたすべての a を包む li を取得して … というように大量の処理が走ってしまいます。

    右にあるセレクタがより特定的であるほど、ブラウザはCSSのプロパティを見つけるときに効率的です。ターゲットに特定のクラスを与えると問題は解決します。

    _good

    .nav__target {}
    

    レイアウトを可能な限り変更しない

    width, height, top, leftのようなプロパティを変更すると、レイアウトは計算されることを必要とし、レンダーツリーをアップデートする必要があります。これらのプロパティの変更を多用すると、位置やサイズを計算して更新するのに重い処理が走ります。

    高負荷なCSSのプロパティ

    いくつかの高負荷なプロパティがあります。とくに負荷が高いのは下記のプロパティです。

    • mix-blend-mode
    • border-radius
    • box-shadow
    • filter
    • position: fixed
    • :nth-child

    使用してはいけないわけではないですが、同じ結果を得られる方法がないか検討するといいかもしれません。

    CSS アニメーションと GPU アクセラレーション

    GPUアクセラレーションは、レンダリング処理をCPU(Central Processing Unit)からGPU(Graphics Processing Unit)に委譲し効率化することです。CPUは全般的なロジックの処理を行えますが、GPUはグラフィックの処理に特化しています。

    ブラウザのGPUアクセラレーションは、要素のテクスチャを独立したComposite Layer としてGPUに転送し、GPUの命令によってテクスチャを操作したり描画データの合成をしたりすることで高速なレンダリングを実現します。このことをCompositing といいます。

    CSS の transform: scale / translate / rotate opacity はGPUで高速に処理でき、要素に対してCompositingを有効にすれば、CPUで処理するよりもスムーズなアニメーションを期待できます。

    will-change で Compositing を有効化

    will-change プロパティは、ほかのプロパティの変更可能性をあらかじめブラウザに伝えて、Compositingなど最適化の準備をするように利用します。


    .target { will-change: transform; transition: transform 2s; transform: scale(1); &.is-active { transform: scale(0); } }

    ただしGPUアクセラレーションはアニメーションする可能性がある要素でなければ意味はなく、ムダにCompositingのためのメモリを消費することになります。なので必要な限られた要素にのみ使うようにします。また、要素が大きかったり、その数が多ければ、処理の思い場合があります。

    ページを高速化の探求と CSS

    CSS からのアプローチでページを高速化するテクニックはそこまで複雑ではないですが、デザインとの兼ね合いもあったり、設計段階で頭に入れておきたいことでもあります。またそのためにもレンダリングパイプラインでやっていることを理解しておく必要があると思います。

    参考

    おわります。

    ピクセルパイプラインのCSSレンダリングパフォーマンスを考える -『CSS』のアイキャッチ画像

    share

    related