content-visibility#
オフスクリーンコンテンツのレンダリングをスキップすることで、初期読み込み時間を改善します。
通常、多くのウェブサイトはそのページに非常に複雑なグラフィカルインターフェースを持っており、一部のコンテンツはユーザーのブラウザの視野範囲を超えている可能性があります。このような場合、CSS の content-visibility
プロパティを使用して、画面外のコンテンツのレンダリングをスキップすることで、ページのレンダリング時間を短縮できます。この機能は最新バージョンの CSS に追加された特性であり、レンダリング性能を向上させる上で大きな影響を与えます。 content-visibility
プロパティには、visible
、auto
、hidden
の 3 つの値があります。しかし、通常は content-visibility
を auto
に設定することで、特にページに大量のオフスクリーンコンテンツが存在する場合に、ページのレンダリング性能を直接向上させることができます。本質的に、このプロパティは 要素の可視性を変更し、そのレンダリング状態を管理します。
content-visibility
の主な機能は、HTML 要素のレンダリングを遅延させることを許可することです。ブラウザはデフォルトで、視覚的に見えるすべての要素をレンダリングしますが、視窓の可視領域の外にある HTML 要素は不可視です。このようにすることで、ブラウザはページのサイズを正しく計算し、全体のページレイアウトとスクロールバーの一貫性を維持します。すべての要素をレンダリングしない場合、スクロールが混乱することになります。なぜなら、ブラウザはページの高さを計算できないからです。
しかし、content-visibility
は、それに割り当てられた要素の高さを 0 として扱い、レンダリング前にその高さを 0 に設定します。これにより、ページの高さとスクロールバーが混乱する可能性があります。ただし、要素またはその子要素に明示的に高さが設定されている場合、この問題は発生しません。明示的に高さが設定されていない場合は、contain-intrinsic-size
を使用して、要素が正しくレンダリングされ、遅延レンダリングの利点を保持できるようにします。
.card {
content-visibility: auto;
contain-intrinsic-size: 200px;
}
contain-intrinsic-size
を使用すると、サイズが設定されていない div(例えば .card)が依然としてスペースを占有し、固有のサイズを持つ単一の子要素のようにレイアウトされることが保証されます。このプロパティは、レンダリングコンテンツの代わりにプレースホルダーとして機能します。
content-visibility: auto
を使用するとページのレンダリング時間を短縮できますが、多くの要素にこのプロパティが設定されている場合、スクロールバーの問題が発生する可能性があります。
content-visibility: auto
以外に、content-visibility
は他の 2 つの値、visible
と hidden
を提供します。これにより、display: none
と非 none 値の間での切り替えが可能になり、要素の明示的な表示と非表示を実現できます。
この場合、content-visibility
は、モーダルウィンドウの表示と非表示など、頻繁に表示または非表示になる要素のレンダリング性能を向上させることができます。content-visibility
は、この性能向上を提供しますが、その隠された値(hidden)の機能が他の値とは異なるためです:
-
display: none
:要素を隠し、そのレンダリング状態を破壊します。これは、要素を再表示することが、新しい要素をレンダリングするのと同じくらいコストがかかることを意味します。 -
visibility: hidden
:要素を隠し、そのレンダリング状態を保持します。これは、文書からその要素を実際に削除するわけではなく、(およびその子ツリー)ページ上の幾何学的スペースを占有し続け、クリック可能です。必要に応じて、隠されていてもレンダリング状態をいつでも更新できます。 -
content-visibility: hidden
:要素を隠し、そのレンダリング状態を保持します。これは、要素が隠れているときの動作がdisplay: none
と同じであることを意味しますが、再表示するコストははるかに低くなります。
References:
https://web.dev/content-visibility/
will-change#
CSS スタイルをレンダリングする前に、CSS レンダラーは準備作業を行う必要があります。なぜなら、特定の CSS プロパティはレンダリングを実現するために多くの準備が必要だからです。これにより、ページがカクつくことがあり、ユーザーに悪い体験を与える可能性があります。
例えば、ウェブページ上のアニメーションは通常、動的要素や他の要素を含めて定期的にレンダリングする必要があります。従来の方法は、CSS の 3D 変換(transform の translate3d () または translateZ ())を使用して GPU アクセラレーションを有効にし、アニメーションをよりスムーズにすることです。しかし、この方法はコストが高く、アニメーションが数百ミリ秒遅延する可能性があります。
現在、CSS の will-change
プロパティを使用して、transform
などのハック技術を使用せずに直接 GPU アクセラレーションを有効にできます。このプロパティは、ブラウザに特定のプロパティを変更することを指示し、必要な最適化を行います。これは、will-change
がヒントであり、それを使用する要素にスタイル上の影響を与えないことを意味します。ただし、新しいスタッキングコンテキストが作成されると、外観に影響を与える可能性があります。
ブラウザが will-change
を持つ要素をレンダリングすると、ブラウザはその要素のために別のレイヤーを作成します。その後、他の最適化と共にその要素のレンダリングを GPU に委任します。つまり、ブラウザは will-change
プロパティを認識し、不透明に関連する将来の変化を最適化します。これにより、アニメーションがよりスムーズになります。なぜなら、GPU アクセラレーションがアニメーションのレンダリングを引き継ぐからです。
will-change
の使用は複雑ではなく、受け入れられる値は次のとおりです:
-
auto
:デフォルト値で、ブラウザは具体的な状況に応じて自動的に最適化を行います。 -
scroll-position
:開発者が要素のスクロール位置を変更することを示します。ブラウザは通常、可スクロール要素の「スクロールウィンドウ」内のコンテンツのみをレンダリングします。しかし、特定のコンテンツがそのウィンドウを超えている場合(ブラウザの可視領域外にある場合)、will-change
が明示的にこの値に設定されていると、レンダリング「スクロールウィンドウ」の周囲のコンテンツが拡張され、より長く、より速いスクロールがスムーズに行われます(要素のスクロールがよりスムーズになります)。 -
contents
:開発者が要素の内容を変更することを示します。ブラウザは通常、あまり変更されない要素の大部分をキャッシュします。しかし、要素の内容が絶えず変化する場合、そのキャッシュを生成および維持することは時間の無駄です。will-change
が明示的にこの値に設定されていると、ブラウザは要素のキャッシュを減少させるか、完全にキャッシュを避けます。つまり、要素を最初から最後まで再レンダリングします。この値を使用する際は、文書ツリーの最末尾で使用することが重要です。なぜなら、この値は宣言された要素の子ノードに適用されるため、文書ツリーの高いノードで使用すると、ページの性能に大きな影響を与える可能性があります。 -
<custom-ident>
:開発者が変更する要素の属性を示します。指定された値が省略形の場合、デフォルトで完全に展開されます。例えば、will-change
の値がpadding
に設定されている場合、すべてのpadding
の属性が補完されます。つまり、will-change: padding-top, padding-right, padding-bottom, padding-left;
となります。
will-change
を使用して、要素が将来変化することを示す#
したがって、will-change
とアニメーションを同時に使用しようとすると、最適化は得られません。したがって、親要素で will-change
を使用し、子要素でアニメーションを使用することをお勧めします。
.animate-element-parent {
will-change: opacity;
}
.animate-element {
transition: opacity .2s linear
}
非アニメーション要素を使用しない#
要素に will-change
を使用すると、ブラウザはその要素を新しいレイヤーに移動し、GPU を介して変換を最適化しようとします。しかし、変換する内容がない場合、リソースの無駄になります。
さらに、will-change
を使用する際は注意が必要です。MDN サイトでは関連する説明が提供されています:
-
will-change
をあまり多くの要素に適用しない:ブラウザはすでに最適化できるすべてのものを最適化しようとしています。より強力な最適化手段はwill-change
と組み合わせて使用される可能性があり、これにより大量のマシンリソースが消費される可能性があります。過度に使用すると、ページの応答が遅くなったり、大量のリソースを占有したりする可能性があります。例えば、* { will-change: transform, opacity; }
-
適度に使用する:通常、要素が初期状態に戻ると、ブラウザは以前の最適化作業を放棄します。しかし、スタイルシートで
will-change
プロパティを明示的に宣言すると、ターゲット要素が頻繁に変化する可能性があることを示すため、ブラウザは以前よりも長く最適化作業を保持します。最良の方法は、要素が変化する前後にスクリプトを使用してwill-change
の値を切り替えることです。 -
will-change
の最適化を早期に適用しない:ページがパフォーマンス面で良好に機能している場合、微小な速度向上を追求するためにwill-change
プロパティを追加すべきではありません。will-change
の設計目的は、既存のパフォーマンス問題を解決することであり、パフォーマンス問題を予防することではありません。過度に使用すると、大量のメモリを消費し、レンダリングプロセスが複雑になり、ブラウザが存在する可能性のある変化プロセスを準備しようとするため、より深刻なパフォーマンス問題を引き起こす可能性があります。 -
十分な作業時間を与える:このプロパティは、ブラウザにどのプロパティが変化する可能性があるかを通知するために使用されます。ブラウザは、変化が発生する前にいくつかの最適化作業を試みることができます。したがって、ブラウザが実際にこれらの最適化作業を行うために十分な時間を与えることが非常に重要です。使用する際は、要素が変化する可能性があることを事前に知る方法を見つけ、
will-change
プロパティを追加する必要があります。
最後に、すべてのアニメーションが完了した後に要素の will-change
を削除することをお勧めします。以下の例は、スクリプトを使用して will-change
プロパティを正しく適用する方法を示しています。ほとんどのシーンで、あなたはこのようにするべきです。
var el = document.getElementById('element');
// マウスがその要素に移動したときにその要素に will-change プロパティを設定
el.addEventListener('mouseenter', hintBrowser);
// CSSアニメーションが終了した後に will-change プロパティをクリア
el.addEventListener('animationEnd', removeHint);
function hintBrowser() {
// CSSアニメーションで変化することがわかっているCSSプロパティ名を記入
this.style.willChange = 'transform, opacity';
}
function removeHint() {
this.style.willChange = 'auto';
}
要素とその内容を文書ツリーの残りの部分からできるだけ独立させる (contain)#
CSS の
contain
プロパティは、レイアウトをブラウザに説明する方法を提供し、パフォーマンスの最適化を行うことができます。しかし、レイアウトに関しては副作用があることもあります。
W3C の CSS Containment Module Level 2 は、前述の content-visibility
プロパティに加えて、もう 1 つのプロパティ contain
を提供しています。このプロパティを使用すると、特定の DOM 要素とその子要素を指定し、それらを全体の DOM ツリー構造から独立させることができます。目的は、ブラウザが部分的な要素のみを再描画、再配置できるようにし、毎回全ページを操作する必要がないようにすることです。つまり、contain
はブラウザに対して、レイアウト、スタイル、描画、サイズ、またはそれらの任意の組み合わせを再計算することを許可します。全ページではなく、DOM の限られた領域に対してです。
実際の使用において、contain
プロパティを使用して、以下の 5 つの値のいずれかを設定することで、要素が文書ツリーからどのように独立するかを規定できます:
layout
:この値は、要素の内部レイアウトが外部の影響を受けず、その要素およびその内容も上位レベルに影響を与えないことを示します。paint
:この値は、要素の子要素がその要素の範囲外に表示されないことを示し、その要素にはコンテンツが溢れない(または溢れても表示されない)ことを示します。size
:この値は、要素のボックスのサイズがその内容から独立していることを示します。つまり、その要素のボックスのサイズを計算する際に、その子要素は無視されます。content
:この値はcontain: layout paint
の省略形です。strict
:この値はcontain: layout paint size
の省略形です。
contain
の size、layout、および paint は、ブラウザのレンダリング計算に異なる影響を与えます:
size
:ブラウザに対して、内容が変更されたときにそのコンテナがページ上の位置を移動させないことを伝えます。layout
:ブラウザに対して、コンテナの子孫がそのコンテナ外の要素のレイアウトを変更しないこと、逆もまた然りであることを伝えます。paint
:ブラウザに対して、コンテナの内容が決してコンテナのサイズを超えて描画されないことを伝えます。コンテナがぼやけている場合、内容はまったく描画されません。
References:
- https://css-tricks.com/lets-take-a-deep-dive-into-the-css-contain-property/
- https://www.smashingmagazine.com/2019/12/browsers-containment-css-contain-property/
フォントによるレイアウトのずれを解決するための font-display
の使用(FOUT)#
ウェブ上で、非システムフォント(@font-face
ルールを通じて導入されたフォント)を使用する場合、ブラウザがウェブフォントをタイムリーに取得できない可能性があり、代わりにバックアップのシステムフォントを使用してテキストをレンダリングすることがあります。これにより、未スタイルのテキストがちらつき、ページ全体のレイアウトがずれる(FOUT)可能性があります。
幸いなことに、CSS の font-display
プロパティは、ブラウザがフォントファイルをどのように読み込み、表示するかを定義し、フォントが読み込まれるか失敗した場合にテキストがバックアップフォントを表示できるようにします。これにより、パフォーマンスが向上し、スタイルのないテキストのちらつきを妥協してテキストを可視化し、白い画面で待つのではなくなります。
CSS の font-display
プロパティには 5 つの値があります:
auto
:デフォルト値。カスタムフォントを使用するテキストは、フォントが読み込まれるまで隠され、表示されません。これはほとんどのブラウザのデフォルト戦略に似ています。block
:フォントに短いブロッキング時間と無限の交換時間を与え、フォントが読み込まれる前に「透明」なテキストを描画します。フォントが読み込まれるとすぐにフォントを切り替えます。特定のフォントでテキストをレンダリングすることがページにとって重要な場合にのみ、block を使用するべきです。swap
:ブロッキング時間は 0、交換時間は無限大で、フォントが読み込まれる前にすぐにテキストを描画します。フォントが正常に読み込まれるとすぐにフォントを切り替えます。特定のフォントでテキストをレンダリングすることがページにとって重要であり、他のフォントでレンダリングしても正しい情報が表示される場合にのみ、swap を使用するべきです。fallback
:短い時間内にカスタムフォントでレンダリングする必要があるテキストが不可視になります。フォントがまだ読み込まれていない場合、スタイルのないテキストが先に読み込まれます。フォントが正常に読み込まれると、テキストは正しくスタイルが適用されます。待機時間が長すぎる場合、ページは常にバックアップフォントを使用します。ユーザーができるだけ早く読み始めることを望み、新しいフォントの読み込みによってテキストスタイルが変わることがユーザー体験を妨げないようにする場合、fallback は良い選択です。optional
:fallback と似ており、非常に短い時間内にテキストが不可視になり、その後スタイルのないテキストが読み込まれます。しかし、optional オプションは、ブラウザがカスタムフォントを使用するかどうかを自由に決定できるようにします。これはブラウザの接続速度に依存します。速度が遅い場合、カスタムフォントは使用されない可能性があります。optional を使用する場合、ブロッキング時間は非常に小さく、交換時間は 0 であるべきです。
@font-face {
font-family: "Open Sans Regular";
font-weight: 400;
font-style: normal;
src: url("fonts/OpenSans-Regular-BasicLatin.woff2") format("woff2");
font-display: swap;
}
References:
- https://iamschulz.com/a-deep-dive-into-webfonts/
- https://simonhearne.com/2021/layout-shifts-webfonts/
- https://css-tricks.com/the-best-font-loading-strategies-and-how-to-execute-them/
- https://calendar.perfplanet.com/2020/a-font-display-setting-for-slow-connections/
- https://css-tricks.com/how-to-load-fonts-in-a-way-that-fights-fout-and-makes-lighthouse-happy/
- https://nooshu.github.io/blog/2021/01/23/the-importance-of-font-face-source-order-when-used-with-preload/
- https://csswizardry.com/2020/05/the-fastest-google-fonts/
- https://www.zachleat.com/web/comprehensive-webfonts/
scroll-behavior#
scroll-behavior は CSSOM View Module が提供する新しい機能で、スムーズなスクロール効果を実現するのに役立ちます。このプロパティは、スクロールボックスにスクロール動作を指定し、ユーザー操作によって発生する他のスクロールには影響を与えません。
scroll-behavior には 2 つの値があります:
auto
:スクロールボックスが即座にスクロールすることを示します。smooth
:スクロールボックスが定義された時間関数を使用して、ユーザーエージェントが定義した時間内にスムーズにスクロールすることを示します。存在する場合、ユーザーエージェントプラットフォームは合意に従うべきです。
GPU レンダリングアニメーションの有効化#
ブラウザは CSS アニメーションの処理と、再配置を適切にトリガーしないアニメーションプロパティの最適化を行っています。パフォーマンスを向上させるために、アニメーション化されたノードをメインスレッドから GPU に移動できます。合成を引き起こすプロパティには、3D 変換(transform: translateZ()
、rotate3d()
など)、アニメーション、変換、透明度、固定位置、will-change
、およびフィルターが含まれます。一部の要素(例えば <video>
、<canvas>
、および <iframe>
)もそれぞれのレイヤーにあります。要素をレイヤーに昇格させる(合成とも呼ばれる)と、アニメーション変換プロパティは GPU 内で完了し、特にモバイルデバイスでパフォーマンスが改善されます。
レンダリングブロック時間の短縮#
大きなスタイルシートを複数のスタイルシートに分解し、主要な CSS ファイルだけがクリティカルパスをブロックし、高優先度でダウンロードし、他のスタイルシートは低優先度でダウンロードします。
<!-- style.css にはページレンダリングに必要な最小限のスタイルのみが含まれています -->
<link rel="stylesheet" href="styles.css" media="all" />
<!-- 次のスタイルシートには、フォームファクターに必要なスタイルのみが含まれています -->
<link rel="stylesheet" href="sm.css" media="(min-width: 20em)" />
<link rel="stylesheet" href="md.css" media="(min-width: 64em)" />
<link rel="stylesheet" href="lg.css" media="(min-width: 90em)" />
<link rel="stylesheet" href="ex.css" media="(min-width: 120em)" />
<link rel="stylesheet" href="print.css" media="print" />
デフォルトでは、ブラウザは指定された各スタイルシートがレンダリングをブロックすると仮定します。メディア属性を追加してメディアクエリを付加することで、ブラウザにスタイルシートを適用するタイミングを知らせます。ブラウザが特定のシーンでのみ使用されることを知っているスタイルシートを見た場合、スタイルはダウンロードされますが、レンダリングはブロックされません。CSS を複数のファイルに分割することで、主要なブロッキングレンダリングファイル(この例では styles.css)のサイズが小さくなり、レンダリングがブロックされる時間が短縮されます。