アニメーションの測定、アニメーション フレームの考え方、ページ全体の滑らかさについて学びます。
スクロールやアニメーション中にページが「途切れる」または「フリーズする」ことはよくあることです。こうしたエクスペリエンスはスムーズではないと Google は考えています。この種の問題に対処するために、Chrome チームはアニメーション検出用のラボツールのサポートを強化するとともに、Chromium 内のレンダリング パイプラインの診断機能を着実に改善しています。
本日は、最近の進捗状況、具体的なツールのガイダンス、今後のアニメーションの滑らかさの指標に関するアイデアについてお話しします。皆様からのフィードバックをお待ちしております。
この投稿では、次の 3 つの主要トピックを取り上げます。
- アニメーションとアニメーション フレームについて簡単に説明します。
- アニメーションの全体的なスムーズさを測定する方法に関する現在の考え方。
- ラボ ツールですぐに活用できる実用的なヒントをいくつかご紹介します。
アニメーションとは
アニメーションはコンテンツに命を吹き込み、コンテンツを動かすことで、特にユーザー操作に応じてアニメーションを作成すると、エクスペリエンスがより自然でわかりやすく、楽しいものになります。
ただし、アニメーションの実装が不十分な場合や、アニメーションの数が多すぎると、ユーザー エクスペリエンスが低下し、まったく楽しくなくなる可能性があります。誰もが、過度に「便利な」遷移効果が追加されたインターフェースを操作したことがあるでしょう。しかし、パフォーマンスが低いと、実際には操作しづらくなることがあります。そのため、一部のユーザーはモーションの低減を好む場合があります。これはユーザーの設定であるため、尊重する必要があります。
アニメーションの仕組み
簡単にまとめると、レンダリング パイプラインは、次の順序付けられたステージで構成されています。
- スタイル: 要素に適用されるスタイルを計算します。
- レイアウト: 各要素のジオメトリと位置を生成します。
- ペイント: 各要素のピクセルをレイヤに塗りつぶします。
- 合成: レイヤを画面に描画します。
アニメーションを定義する方法は多数ありますが、基本的には次のいずれかの方法で動作します。
- レイアウト プロパティを調整する。
- ペイント プロパティの調整。
- 複合プロパティの調整。
これらのステージは順次であるため、パイプラインの下流にあるプロパティの観点からアニメーションを定義することが重要です。プロセスの早い段階で更新を行うほど、費用が増加し、スムーズに進まない可能性が高くなります。(詳しくは、レンダリング パフォーマンスをご覧ください)。
レイアウト プロパティをアニメーション化することは便利ですが、そうすることでコストが発生します。そのコストはすぐには明らかにならない場合もあります。アニメーションは、可能な限り複合プロパティの変更に関して定義する必要があります。
宣言型 CSS アニメーションまたはウェブ アニメーションの使用を定義し、複合プロパティをアニメーション化することは、スムーズで効率的なアニメーションを実現するための重要な第一歩です。ただし、効率的なウェブ アニメーションにもパフォーマンスの上限があるため、これだけではスムーズな動作が保証されるわけではありません。そのため、常に測定することが重要です。
アニメーション フレームとは
ページの視覚的な表現が更新されるまでには時間がかかります。視覚的な変化により、新しいアニメーション フレームが作成され、最終的にユーザーのディスプレイにレンダリングされます。
一定の間隔で更新を表示するため、視覚的な更新はバッチ処理されます。多くのディスプレイは、1 秒間に 60 回(60 Hz)など、一定の間隔で更新されます。最新のディスプレイでは、リフレッシュ レートが高くなります(90 ~ 120 Hz が一般的になってきています)。多くの場合、これらのディスプレイは必要に応じてリフレッシュ レートを積極的に調整したり、完全に可変のフレームレートを提供したりできます。
ゲームやブラウザなどのアプリケーションの目標は、これらのバッチ処理されたビジュアル アップデートをすべて処理し、期限内に視覚的に完全なアニメーション フレームを生成することです。この目標は、ネットワークからコンテンツをすばやく読み込む、JavaScript タスクを効率的に実行するなど、他の重要なブラウザタスクとはまったく異なります。
ある時点で、ディスプレイによって割り当てられた期限内にすべてのビジュアル アップデートを完了することが難しくなる場合があります。この場合、ブラウザはフレームをドロップします。画面が黒くなることはなく、繰り返し再生されるだけです。少し長く、同じアニメーション フレームが表示されます。これは、前のフレーム機会で表示したアニメーション フレームと同じです。
これはよくあることです。特に静的コンテンツやドキュメントのようなコンテンツの場合、認識できるとは限りません。これは特にウェブ プラットフォームでよく見られます。フレーム落ちは、アニメーションなどの重要な視覚的更新があり、滑らかな動きを示すためにアニメーション更新の安定したストリームが必要な場合にのみ明らかになります。
アニメーション フレームに影響を与えるもの
ウェブ デベロッパーは、ブラウザが視覚的な更新をすばやく効率的にレンダリングして表示する能力に大きな影響を与えることができます。
例:
- 対象デバイスで迅速にデコードできないほどサイズが大きいコンテンツや、リソースを大量に消費するコンテンツを使用している。
- レイヤが多すぎるため、GPU メモリが多すぎる。
- 過度に複雑な CSS スタイルやウェブ アニメーションを定義する。
- 高速レンダリングの最適化を無効にする設計アンチパターンを使用している。
- メインスレッドでの JS 作業が多すぎると、タスクに時間がかかり、視覚的な更新を妨げることがあります。
では、アニメーション フレームが期限を過ぎてフレームがドロップされたことをどのように把握すればよいでしょうか。
1 つの方法として requestAnimationFrame()
ポーリングを使用できますが、いくつかの欠点があります。requestAnimationFrame()
(rAF)は、ブラウザにアニメーションの実行を指示し、レンダリング パイプラインの次のペイント ステージの前にアニメーションを実行する機会を要求します。コールバック関数が想定した時間に呼び出されなかった場合、ペイントは実行されず、1 つ以上のフレームがスキップされたことを意味します。rAF が呼び出される頻度をポーリングしてカウントすることで、「フレーム / 秒」(FPS)指標を計算できます。
let frameTimes = [];
function pollFramesPerSecond(now) {
frameTimes = [...frameTimes.filter(t => t > now - 1000), now];
requestAnimationFrame(pollFramesPerSecond);
console.log('Frames per second:', frameTimes.length);
}
requestAnimationFrame(pollFramesPerSecond);
requestAnimationFrame()
ポーリングの使用は、いくつかの理由からおすすめしません。
- 各スクリプトで独自のポーリング ループを設定する必要があります。
- クリティカル パスをブロックできます。
- rAF ポーリングが高速であっても、連続して使用すると、長いアイドル ブロック(1 フレームを超えるブロック)を
requestIdleCallback()
がスケジュールできなくなる可能性があります。 - 同様に、長いアイドル ブロックがないと、ブラウザは他の長時間実行タスク(より長いガベージ コレクション、その他のバックグラウンド処理や投機的作業など)をスケジュールできなくなります。
- ポーリングのオンとオフを切り替えると、フレーム バジェットを超過するケースを見逃してしまいます。
- ブラウザが可変の更新頻度を使用している場合(電源や可視性のステータスなどによる場合)は、ポーリングで誤検出が報告されます。
- そして最も重要な点として、実際にはすべてのタイプのアニメーション アップデートをキャプチャするわけではありません。
メインスレッドで処理する作業が多すぎると、アニメーション フレームの表示に影響する可能性があります。Jank サンプルで、rAF 駆動型アニメーションがメインスレッドでの処理(レイアウトなど)が多すぎると、フレーム落ち、rAF コールバックの減少、FPS の低下につながる仕組みをご確認ください。
メインスレッドが遅延すると、視覚的な更新が途切れ始めます。ジャンクだ!
多くの測定ツールは、メインスレッドがタイムリーに生成し、アニメーション フレームをスムーズに実行する機能に重点を置いてきました。しかし、それだけではありません。たとえば次のようになります。
上の動画は、長いタスクをメインスレッドに定期的に挿入するページを示しています。これらの長いタスクは、ページが特定の種類のビジュアル アップデートを提供する能力を完全に損ないます。左上の requestAnimationFrame()
が報告する FPS が 0 に低下しているのは、このためです。
こうした長いタスクにもかかわらず、ページはスムーズにスクロールし続けます。これは、最新のブラウザでは、スクロールがスレッド化されることが多いため、コンポーザで完全に駆動されるためです。
これは、メインスレッドで多くのフレームがドロップされているにもかかわらず、コンポーザ スレッドでスクロールのフレームが正常に配信されている例です。長いタスクが完了すると、メインスレッドのペイント更新では視覚的な変化は生じません。rAF ポーリングではフレーム ドロップが 0 になることが提案されましたが、視覚的には違いに気づくことができません。
アニメーション フレームの場合、話はそれほど単純ではありません。
アニメーション フレーム: 重要な更新
上記の例は、ストーリーに requestAnimationFrame()
以外の要素があることを示しています。
では、アニメーションの更新とアニメーション フレームはどのような場合に重要になるのでしょうか。以下に、現在検討している基準をいくつか示します。ご意見をお寄せいただければ幸いです。
- メインスレッドとコンポーザ スレッドの更新
- ペイントの更新がない
- アニメーションの検出
- 質と量
メインスレッドとコンポジタ スレッドの更新
アニメーション フレームの更新がブール値ではありません。フレームが完全にドロップされたり、完全に表示されたりするだけというわけではありません。アニメーション フレームが部分的に表示される理由はいくつかあります。つまり、古いコンテンツと新しいビジュアル アップデートが同時に表示される場合があります。
最も一般的な例は、ブラウザがフレーム期限内に新しいメインスレッドの更新を生成できないが、新しいコンポジタ スレッドの更新がある場合です(以前のスレッド スクロールの例など)。
宣言型アニメーションを使用して複合プロパティをアニメーション化することをおすすめする重要な理由の一つは、メインスレッドがビジー状態の場合でも、アニメーションをコンポジタ スレッドによって完全に駆動できることです。このようなタイプのアニメーションでは、引き続き効率的かつ並行して視覚的な更新を生成できます。
一方、メインスレッドのアップデートが、複数のフレーム期限を過ぎた後にようやく表示可能になった場合もあります。ブラウザは一部更新されますが、最新版ではない可能性があります。
大まかに言えば、新しいビジュアル アップデートが一部含まれるフレーム(新しいビジュアル アップデートがすべて含まれていないフレーム)は、部分フレームと見なされます。部分的なフレームは非常に一般的です。理想的には、部分更新には少なくともアニメーションなどの最も重要なビジュアル更新が含まれますが、これはアニメーションがコンポジタ スレッドによって駆動されている場合にのみ可能です。
ペイントの更新がない
別のタイプの部分更新は、画像などのメディアがフレーム表示までにデコードとラスタライズを完了していない場合です。
また、ページが完全に静的である場合でも、高速スクロール中にブラウザが視覚的な更新のレンダリングに追いつかないことがあります。これは、GPU メモリを節約するために、表示されるビューポート外のコンテンツのピクセル レンダリングが破棄される可能性があるためです。ピクセルをレンダリングするには時間がかかります。指のフリックなど、大きなスクロール後にすべてをレンダリングするのに、1 フレームよりも時間がかかる場合があります。これは一般に「チェッカーボード表示」と呼ばれます。
フレーム レンダリングの機会ごとに、最新のビジュアル アップデートのどれだけが実際に画面に表示されたかをトラッキングできます。多くのフレーム(または時間)にわたってこの処理を行う能力を測定することを、一般的にフレーム スループットと呼びます。
GPU が本当に遅い場合、ブラウザ(またはプラットフォーム)は視覚的な更新を試みるレートをスロットリングし始め、有効なフレームレートが低下することもあります。技術的にはフレーム更新のドロップ回数を減らすことができますが、それでもフレーム スループットは低下したように見えます。
とはいえ、フレーム スループットの低下が必ずしも悪いわけではありません。ページがほとんどアイドル状態にあり、アクティブなアニメーションがない場合は、低いフレームレートでも高いフレームレートと同様に視覚的に魅力的です(バッテリーも節約できます)。
フレーム スループットが重要となるのは、どのような場合ですか?
アニメーションの検出
高いフレーム スループットは、重要なアニメーションがある期間に特に重要です。アニメーションの種類によって、特定のスレッド(メイン、コンポジタ、ワーカー)からの視覚的な更新に依存するため、その視覚的な更新は、そのスレッドが期限内に更新を提供することに依存します。あるスレッドの更新に依存するアクティブなアニメーションがある場合、そのスレッドはスムーズさに影響すると言います。
アニメーションには、定義や検出が容易なものとそうでないものがあります。宣言型アニメーション(ユーザー入力ドリブンのアニメーション)は、アニメーション可能スタイル プロパティの定期的な更新として実装される JavaScript ドリブンのアニメーションよりも、定義が明確です。
requestAnimationFrame()
を使用しても、すべての rAF 呼び出しが必ずしも視覚的な更新やアニメーションを生成するとは限りません。たとえば、フレームレートを追跡するためだけに rAF ポーリングを使用する場合(上記を参照)、視覚的な更新がないため、スムーズさの測定に影響することはありません。
質 vs 量
最後に、アニメーションとアニメーション フレームの更新を検出しても、アニメーションの更新回数しかキャプチャされず、品質はキャプチャされないため、これは全体像の一部にすぎません。
たとえば、動画の視聴中に 60 fps の安定したフレームレートが表示される場合があります。技術的には完全にスムーズですが、動画自体のビットレートの低さやネットワーク バッファリングの問題が原因である可能性があります。これはアニメーションの滑らかさの指標では直接キャプチャされませんが、ユーザーにとって不快な場合があります。
また、<canvas>
を活用するゲーム(場合によっては、画面外キャンバスなどの手法を使用して安定したフレームレートを確保)は、高品質のゲームアセットをシーンに読み込むことができないか、レンダリング アーティファクトを示さないにもかかわらず、アニメーション フレームの点で技術的に完全に滑らかになる場合があります。
もちろん、サイトにアニメーションがまったくない場合もあります。
当時としてはかなりクールだったと思います。
単一のアニメーション フレームの状態
フレームが部分的に表示される場合や、フレーム落ちが発生しても滑らかさに影響しない可能性があるため、各フレームには完全性スコアまたは滑らかさスコアがあると考えるようになりました。
単一アニメーション フレームの状態を解釈する方法の範囲は、ベストケースからワーストケースの順に次のとおりです。
更新不要 | アイドル時間、前のフレームの繰り返し。 |
完全に表示 | メインスレッドの更新が期限内に commit されたか、メインスレッドの更新が不要でした。 |
一部表示 | コンポジターのみ。遅延したメインスレッドの更新に視覚的な変化はありません。 |
一部表示 | コンポジターのみ。メインスレッドには視覚的な更新がありましたが、その更新には滑らかさに影響するアニメーションが含まれていませんでした。 |
一部表示 | コンポジタのみ。メインスレッドでスムーズさに影響する視覚的な更新が行われたが、以前の古いフレームが到着し、代わりに使用された。 |
部分的に提示 | コンポジタのみ。必要なメイン アップデートがなく、コンポジタのアップデートにスムーズさに影響するアニメーションがある。 |
部分的に提示 | コンポーザにのみ影響しますが、コンポーザのアップデートにはスムーズさに影響するアニメーションはありません。 |
フレーム落ち | 更新なし。コンポジタの更新は不要で、メインは遅延しました。 |
フレーム落ち | コンポジタの更新が望まれていたが、遅延した。 |
古いフレーム | 更新が求められ、レンダラによって生成されたが、GPU は vsync 期限までに更新を表示しなかった。 |
これらの状態をスコアに変換できます。このスコアの解釈方法としては、ユーザーが確認できる確率を「確率」とみなすことが挙げられます。1 つのフレーム落ちはほとんど目立ちませんが、連続して発生し、スムーズさに影響するフレーム落ちは目立ちます。
まとめ: ドロップフレームの割合の指標
各アニメーション フレームの状態を深く掘り下げる必要がある場合もありますが、エクスペリエンスに「概要」スコアを割り当てるだけでも役立ちます。
フレームは部分的に表示される場合があり、フレーム更新が完全にスキップされても実際にスムーズさに影響しない場合があるため、フレームのカウントではなく、ブラウザが重要なときに視覚的に完全な更新を提供できない程度に重点を置いています。
メンタルモデルは、次のように変化する必要があります。
- フレームレート:
- アニメーションの欠落や重要な更新を検出し、
- 特定の期間における減少率。
重要な更新を待機している時間の割合が重要です。これは、ユーザーがウェブ コンテンツの滑らかさを実際に体験する自然な方法と一致していると考えています。これまでは、次の指標を初期セットとして使用してきました。
- Average Percent Dropped:タイムライン全体における、アイドル状態でないすべてのアニメーション フレームのドロップ率
- フレーム ドロップの最悪の割合: 1 秒のスライディング ウィンドウで測定されます。
- ドロップしたフレームの割合の 95 パーセンタイル: 1 秒のスライディング ウィンドウで測定されます。
このスコアは現在、一部の Chrome デベロッパー ツールで確認できます。これらの指標は全体的なフレーム スループットにのみ焦点を当てていますが、フレーム レイテンシなどの他の要素も評価しています。
デベロッパー ツールで実際に試してみましょう。
パフォーマンス HUD
Chromium には、フラグ(chrome://flags/#show-performance-metrics-hud
)の背後に隠された、すっきりとしたパフォーマンス HUD があります。この HUD では、Core Web Vitals などのライブスコアを確認できます。また、時間の経過に伴うフレーム落ち率に基づくアニメーションの滑らかさに関するいくつかの実験的な定義も確認できます。
フレーム レンダリング統計情報
レンダリング設定で DevTools の [フレーム レンダリングの統計情報] を有効にすると、新しいアニメーション フレームのライブビューが表示されます。このビューでは、部分的な更新と完全にドロップされたフレームの更新を区別するために色分けされています。報告される fps は、完全に表示されたフレームのみです。
DevTools のパフォーマンス プロファイル記録のフレーム ビューア
DevTools の [Performance] パネルには、以前から Frames ビューアが追加されました。しかし、最新のレンダリング パイプラインの実際の動作とは少しずれていたため、最近、最新の Chrome Canary でも多くの改善が加えられています。これにより、アニメーションの問題のデバッグが大幅に容易になると考えられます。
フレームビューアのフレームは、vsync 境界とより正確に位置合わせされ、ステータスに基づいて色分けされます。上記のニュアンスのすべてを完全に可視化することはまだできませんが、近い将来、さらに追加する予定です。
Chrome トレース
さらに、詳細を深く分析するためのツールである Chrome トレースを使用すると、新しい Perfetto UI(about:tracing
)を介して「ウェブ コンテンツ レンダリング」トレースを記録し、Chrome のグラフィック パイプラインを深く掘り下げることができます。大変な作業ですが、最近 Chromium に追加された機能により、作業が容易になりました。利用可能な機能の概要については、フレームのライフサイクルのドキュメントをご覧ください。
トレース イベントを使用すると、次のことを明確に判断できます。
- 実行中のアニメーション(
TrackerValidation
という名前のイベントを使用)。 - アニメーション フレームの正確なタイムラインを取得する(
PipelineReporter
という名前のイベントを使用)。 - アニメーションの更新がぎくしゃくしている場合は、
PipelineReporter
イベント内のイベントの内訳を使用して、アニメーションの高速実行を妨げている原因を正確に特定します。 - 入力ドリブンのアニメーションの場合は、視覚的な更新が得られるまでの時間を確認します(
EventLatency
という名前のイベントを使用)。
次のステップ
Web Vitals イニシアチブは、ウェブで優れたユーザー エクスペリエンスを構築するための指標とガイダンスを提供することを目的としています。Total Blocking Time(TBT)などのラボベースの指標は、潜在的なインタラクティビティの問題を検出して診断するために不可欠です。Google では、アニメーションの滑らかさに関して、同様のラボベースの指標を設計する予定です。
個々のアニメーション フレームデータに基づいて包括的な指標を設計するアイデアが浮かび上がりましたら、引き続きお知らせいたします。
今後は、ラボだけでなく現場の実際のユーザーに対して、アニメーションの滑らかさをパフォーマンスに優れた方法で測定できる API を設計したいと考えています。最新情報もぜひご確認ください。
フィードバック
アニメーションの滑らかさを測定するための Chrome の最新の改善とデベロッパー ツールをご紹介します。これらのツールをお試しいただき、アニメーションをベンチマークして、結果をお知らせください。
コメントは、web-vitals-feedback Google グループに送信してください。件名には「[スムーズネス指標]」と記載してください。ご意見をお待ちしております。