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