アニメーションの滑らかさの指標に向けて

アニメーションの測定、アニメーション フレームの考え方、ページ全体の滑らかさについて説明します。

Behdad Bakhshinategh
Behdad Bakhshinategh
Jonathan Ross
Jonathan Ross
Michal Mocny
Michal Mocny

スクロールやアニメーション中にページが「途切れ」または「フリーズ」したことがあります。こうしたエクスペリエンスはスムーズではないと言えます。この種の問題に対処するため、Chrome チームはアニメーション検出のラボツールのサポートを強化するとともに、Chromium 内のレンダリング パイプラインの診断機能の改善に取り組んできました。

最近の進捗状況を共有し、具体的なツールのガイダンスを提供し、将来のアニメーションの滑らかさの指標に関するアイデアについて説明します。ぜひフィードバックをお寄せください。

今回の投稿では、次の 3 つのトピックを取り上げます。

  • アニメーションとアニメーション フレームについて簡単に説明します。
  • アニメーションの全体的な滑らかさの測定に関する Google の現在の見解。
  • ラボのツールで今すぐ活用できる実践的な提案をいくつか紹介します。

アニメーションとは

アニメーションでコンテンツに命を吹き込むアニメーションは、特にユーザーの操作に応じてコンテンツを動かすことで、より自然でわかりやすく、楽しいものにします。

しかし、アニメーションの実装が不適切であったり、アニメーションを追加しすぎたりすると、エクスペリエンスが低下し、まったく楽しくなくなる可能性があります。「有用な」遷移効果が多くなりすぎたインターフェースを操作したことのある方もいるでしょう。パフォーマンスが低いと、実際にエクスペリエンスに対して悪影響を及ぼします。したがって、一部のユーザーは実際にはモーションの低減を好む可能性があります。これは優先されるべきユーザー設定です。

アニメーションの仕組み

簡単に説明すると、レンダリング パイプラインはいくつかの連続したステージで構成されています。

  1. スタイル: 要素に適用されるスタイルを計算します。
  2. レイアウト: 各要素のジオメトリと位置を生成します。
  3. ペイント: 各要素のピクセルをレイヤに塗りつぶします。
  4. Composite: レイヤを画面に描画します。

アニメーションを定義する方法は多数ありますが、基本的には次のいずれか 1 つを通じて機能します。

  • レイアウト プロパティを調整する
  • ペイント プロパティの調整
  • 複合プロパティの調整

これらのステージは順次であるため、パイプラインのさらに下流にあるプロパティの観点からアニメーションを定義することが重要です。更新がプロセス内で早期に行われるほど、コストは高くなり、スムーズに行える可能性は低くなります。(詳しくは、レンダリング パフォーマンスをご覧ください)。

レイアウト プロパティをアニメーション化すると便利な場合もありますが、そのコストがすぐに明らかでない場合でも、コストが発生します。アニメーションは、可能な限り複合プロパティの変更の観点から定義する必要があります。

スムーズで効率的なアニメーションを実現するには、宣言型 CSS アニメーションまたはウェブ アニメーションを定義し、複合プロパティをアニメーション化することが重要です。しかし、それでも、効率的なウェブ アニメーションであってもパフォーマンスには限界があるため、これだけで滑らかさが保証されるわけではありません。そのため、常に測定することが重要です。

アニメーション フレームとは

ページの視覚的表現が更新されるまでには時間がかかります。視覚的な変化により新しいアニメーション フレームが作成され、最終的にユーザーのディスプレイにレンダリングされます。

一定の間隔で更新が表示されるため、視覚的な更新はバッチ処理されます。多くのディスプレイは、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() が長時間アイドル状態のブロック(1 フレームを超えるブロック)を継続的に使用すると、スケジュールできなくなる可能性があります。
  • 同様に、長いアイドル ブロックがないと、ブラウザは他の長時間実行タスク(長時間のガベージ コレクションやその他のバックグラウンド処理や投機的処理など)をスケジュールできなくなります。
  • ポーリングのオンとオフが切り替わっていると、フレーム バジェットを超えたケースを見逃してしまう可能性があります。
  • ブラウザが可変の更新頻度を使用している場合(電源や表示の状態などにより)、ポーリングで偽陽性が報告されます。
  • 最も重要な点は、すべての種類のアニメーションの更新を実際にキャプチャするわけではないことです。

メインスレッドに対する処理が多すぎると、アニメーション フレームの表示に影響する可能性があります。ジャンク サンプルを確認して、rAF 駆動型アニメーションがメインスレッドで過度に作業すると(レイアウトなど)、フレーム落ち、rAF コールバックの減少、FPS の低下にどのようにつながるかを確認してください。

メインスレッドが行き詰まると、視覚的な更新が途切れ始めます。これはジャンクです。

多くの測定ツールは、メインスレッドがタイムリーに処理を進めることと、アニメーション フレームをスムーズに実行できるようにすることに重点を置いています。しかし、これだけではありません。たとえば次のようになります。

上記の動画は、長いタスクをメインスレッドに定期的に挿入するページを示しています。このような時間のかかるタスクは、特定の種類のビジュアル更新を提供するページの機能を完全に台無しにし、左上隅では、requestAnimationFrame() で報告される FPS が 0 に、対応する低下を示しています。

このような時間のかかるタスクにもかかわらず、ページはスムーズにスクロールし続けます。これは、最新のブラウザではスクロールが多くの場合スレッド化され、コンポジタによって完全に制御されるためです。

この例では、メインスレッドには同時に多数のフレーム落ちがあるにもかかわらず、コンポジタ スレッドでは正常に配信されたスクロール フレームも多数存在します。長いタスクが完了すると、メインスレッドのペイント更新が提供する視覚的な変更はありません。rAF ポーリングはフレームのドロップを 0 にすることを提案しましたが、視覚的には違いに気づかないでしょう。

アニメーション フレームの場合、ストーリーはそれほど単純ではありません。

アニメーション フレーム: 重要な更新

上記の例は、requestAnimationFrame() 以外にも多くの要素があることを示しています。

では、アニメーションの更新とアニメーション フレームはどのような場合に重要でしょうか。Google では現在、次のような基準を検討しており、フィードバックをお待ちしています。

  • メイン スレッドとコンポジタ スレッドの更新
  • ペイントのアップデートがない
  • アニメーションの検出
  • 質と量

メイン スレッドとコンポジタ スレッドの更新

アニメーション フレームの更新はブール値ではありません。フレームが完全にドロップされたり、完全に表示されたりするだけというわけではありません。アニメーション フレームが部分的に表示されるには、多くの理由があります。つまり、古いコンテンツが表示されると同時に、新しいビジュアル アップデートが表示される可能性があります。

この最も一般的な例は、ブラウザがフレーム期限内に新しいメインスレッドの更新を生成できないが、新しいコンポジタ スレッドの更新がある場合です(前述のスレッド スクロールの例など)。

宣言型アニメーションを使用して複合プロパティをアニメーション化する重要な理由の 1 つは、それにより、メインスレッドがビジー状態であっても、コンポジタ スレッドでアニメーションを完全に駆動できることです。このタイプのアニメーションでは、ビジュアルの更新を継続的に効率的に並行して生成できます。

一方、メインスレッドの更新がようやくプレゼンテーションのために利用可能になったとしても、いくつかのフレーム期限が過ぎた後でなければ利用できないことがあります。この場合、ブラウザに新しいアップデートがいくつか適用されますが、最新のバージョンであるとは限りません。

大まかに言うと、新しいビジュアル アップデートがないフレームを部分フレームと見なします。部分フレームはかなり一般的です。部分更新には、少なくともアニメーションなどの最も重要な視覚的更新が含まれるのが理想的ですが、これはアニメーションがコンポジタ スレッドによって駆動される場合にのみ行われます。

ペイントのアップデートがない

部分更新のもう 1 つのタイプは、画像などのメディアがフレーム表示の時間内にデコードとラスタライズを完了していない場合です。

あるいは、ページが完全に静的な場合でも、高速スクロール中はブラウザがレンダリングに遅れをとっている可能性があります。これは、GPU メモリを節約するために、表示可能なビューポートの外にあるコンテンツのピクセル レンダリングが破棄される可能性があるためです。ピクセルのレンダリングには時間がかかります。また、大きなスクロール(指のフリングなど)の後にすべてをレンダリングするには、1 フレームより時間がかかる場合があります。これは一般に「チェッカーボード」と呼ばれます。

フレーム レンダリングの機会ごとに、最新のビジュアル アップデートがどの程度画面に実際に適用されたかを追跡できます。これを多くのフレーム(または時間)にわたって測定することを、フレーム スループットと広く知られています。

GPU の処理速度が非常に遅くなると、ブラウザ(またはプラットフォーム)がビジュアル アップデートを試みるレートを抑制し、有効なフレームレートが低下することもあります。技術的にはフレームの更新のドロップ数を減らすことはできますが、視覚的には依然としてフレーム スループットの低下のように見えます。

しかし、低フレーム スループットがあらゆる種類の問題に悪影響を及ぼすわけではありません。ページがほとんどアイドル状態で、アクティブなアニメーションがない場合、低フレームレートは高フレームレートと同様に視覚的に魅力があります(また、バッテリーを節約できます)。

では、フレーム スループットが重要となるのはどのような場合でしょうか。

アニメーションの検出

フレーム スループットが高いことは、重要なアニメーションがあるときに特に重要です。異なるアニメーション タイプは、特定のスレッド(メイン、コンポジタ、ワーカー)からのビジュアル更新に依存します。したがって、ビジュアル更新は期限内に更新を提供するスレッドに依存します。そのスレッドの更新に依存するアクティブなアニメーションがある場合は、特定のスレッドが滑らかさに影響すると言います。

アニメーションには、他の種類よりも定義と検出が簡単なものがあります。宣言型アニメーション、つまりユーザー入力駆動型アニメーションは、アニメーション化可能なスタイル プロパティの定期的な更新として実装される JavaScript 駆動型アニメーションよりも明確に定義できます。

requestAnimationFrame() を使用する場合でも、すべての rAF 呼び出しが必ずしも視覚的な更新やアニメーションを生成するとは限りません。たとえば、上記のようにフレームレートをトラッキングするためだけに rAF ポーリングを使用しても、視覚的な更新がないため、スムーズさの測定結果には影響しません。

質と量

最後に、アニメーションとアニメーション フレームの更新の検出は、品質ではなくアニメーションの更新の量しか取得しないため、ストーリーの一部にすぎません。

たとえば、動画の視聴中にフレームレートが 60 fps で固定されることがあります。技術的には完全にスムーズですが、動画自体のビットレートが低いか、ネットワーク バッファリングに問題がある可能性があります。これは、アニメーションの滑らかさの指標で直接取得されるわけではありませんが、ユーザーにとって不快な場合があります。

あるいは、<canvas> を利用するゲームは(安定したフレームレートを確保するためにオフスクリーン キャンバスなどの技術を使用することもあります)は、高品質のゲームアセットをシーンに読み込めなかったり、レンダリング アーティファクトが表示されなかったりしても、アニメーション フレームに関しては完全にスムーズである可能性があります。

もちろん、サイトに悪質なアニメーションが含まれていることもあります 🙂?

建設中の旧校の GIF

当時はとてもかっこいいだったからね!

単一のアニメーション フレームの状態

フレームが部分的に表示されることや、滑らかさに影響しない方法でフレーム落ちが発生する可能性があるため、Google は各フレームが完全性スコアまたはスムーズネス スコアを持つと考えるようになりました。

以下に、単一のアニメーション フレームの状態の解釈方法を、最良のものから順に説明します。

更新の必要なし アイドル時間、前のフレームの繰り返し。
すべて表示 メインスレッドの更新が期限内に commit されたか、メインスレッドの更新が意図されていませんでした。
一部表示 コンポジタのみ。遅延したメインスレッドの更新に視覚的な変化はありません。
一部表示 コンポジタのみ。メインスレッドに視覚的な更新がありましたが、その更新には滑らかさに影響を与えるアニメーションが含まれていませんでした。
一部表示 コンポジタのみ。メインスレッドに滑らかさに影響を与える視覚的な更新がありましたが、以前に古いフレームが到着し、代わりに使用されました。
一部表示 コンポジタのみ。必要なメイン アップデートがなく、コンポジタの更新に滑らかさに影響するアニメーションが含まれています。
一部表示 コンポジタのみ。ただし、コンポジタの更新には、滑らかさに影響するアニメーションがありません。
フレーム落ち 更新はありません。コンポジタの更新が不要になったため、main が遅延しました。
フレーム落ち コンポジタの更新が必要でしたが、遅延が発生しました。
フレームが古い 更新が必要で、レンダラによって作成されましたが、vsync の期限までに GPU が更新しませんでした。

これらの状態をスコアに変えることができます。このスコアを解釈する方法の 1 つは、ユーザーがこれを観測できる可能性と考えることです。1 つのフレームがドロップされてもあまり観察できないことがありますが、一連のフレームのドロップが連続して滑らかになることは確実にあります。

まとめ: ドロップしたフレームの割合の指標

各アニメーション フレームの状態を深く掘り下げる必要がある場合もありますが、エクスペリエンスに簡単な「一目でわかる」スコアを割り当てるだけでも便利です。

フレームが部分的に表示されることがあり、完全にスキップされたフレームの更新であっても実際には滑らかさに影響しない可能性があるため、フレームのカウントのみではなく、ブラウザが重要なときに視覚的に完全な更新を提供できない範囲に重点を置きます。

メンタルモデルは、

  1. フレーム/秒
  2. 欠落している重要なアニメーションの更新を検出し、
  3. 特定の期間における減少率

重要なのは、重要な更新の待機時間の割合です。これは、ユーザーがウェブ コンテンツのスムーズさを実感する自然な方法と一致していると考えています。これまでは、最初の指標セットとして以下を使用してきました。

  • 平均ドロップ率: タイムライン全体のすべての非アイドル状態のアニメーション フレーム
  • フレーム落ち率の最悪のケース: 1 秒のスライディング ウィンドウで測定。
  • ドロップされたフレームの 95 パーセンタイル: 1 秒間のスライディング ウィンドウで測定。

このスコアは一部の Chrome デベロッパー ツールで確認できます。これらの指標は全体的なフレーム スループットのみに焦点を当てていますが、フレーム レイテンシなどの他の要素も評価しています。

デベロッパー ツールで実際に試す

パフォーマンス HUD

Chromium には、フラグ(chrome://flags/#show-performance-metrics-hud)によって優れたパフォーマンス HUD が隠されています。そこには、Core Web Vitals などのライブスコアや、時間の経過に伴うドロップされたフレームの割合に基づくアニメーションの滑らかさに関する試験運用版の定義がいくつか用意されています。

パフォーマンス HUD

フレーム レンダリングの統計情報

DevTools の [Rendering] 設定で [Frame Rendering Stats] を有効にして、新しいアニメーション フレームのライブビューを確認します。新しいアニメーション フレームは、部分的な更新と完全にドロップされたフレームの更新を区別するために色分けされています。表示される fps は、完全に表示されたフレームのみの値です。

フレーム レンダリングの統計情報

DevTools のパフォーマンス プロファイル記録の Frame Viewer

DevTools の [パフォーマンス] パネルには、以前からフレーム ビューアがありました。しかし、最新のレンダリング パイプラインの実際の動作とは少しずれていました。最近では、最新の Chrome Canary でも多くの改善が行われており、アニメーションの問題のデバッグが大幅に容易になると考えています。

本日、フレーム ビューアのフレームが vsync の境界に合わせて調整され、ステータスに基づいて色分けされるようになりました。現時点で上記のすべてのニュアンスを完全に可視化することはできませんが、今後さらに追加する予定です。

Chrome DevTools のフレーム ビューア

Chrome トレース

最後に、詳細を調べるためのツールである Chrome トレースを使用すると、新しい Perfetto UI(または about:tracing)を介して「ウェブ コンテンツ レンダリング」トレースを記録し、Chrome のグラフィック パイプラインを詳しく調べることができます。大変な作業ですが、最近 Chromium にはそれを簡単にするための機能がいくつか追加されました。内容の概要については、フレームのライフサイクルのドキュメントをご覧ください。

トレース イベントでは、次の情報を確実に特定できます。

  • 実行中のアニメーション(TrackerValidation という名前のイベントを使用)。
  • アニメーション フレームの正確なタイムラインを取得する(PipelineReporter という名前のイベントを使用)。
  • 不自然なアニメーションの更新では、PipelineReporter イベント内のイベントの内訳を使用して、アニメーションの実行速度を妨げている原因を正確に特定します。
  • 入力ドリブン アニメーションの場合は、(EventLatency という名前のイベントを使用して)ビジュアル アップデートを取得するのにかかる時間を確認します。

Chrome Tracing パイプライン レポーター

次のステップ

ウェブに関する指標イニシアチブは、ウェブ上で優れたユーザー エクスペリエンスを構築するための指標とガイダンスを提供することを目的としています。Total Blocking Time(TBT)などのラボベースの指標は、潜在的なインタラクティビティの問題を検出して診断するために不可欠です。今後、アニメーションの滑らかさに関するラボベースの同様の指標を設計する予定です。

個々のアニメーション フレーム データに基づいて包括的な指標を設計するためのアイデアを引き続き検討してまいりますので、今後も更新情報をお知らせいたします。

将来的には、フィールドとラボの実際のユーザーについて、アニメーションの滑らかさのパフォーマンスを効率よく測定できる API も設計したいと考えています。今後の最新情報にもご注目ください。

フィードバック

アニメーションの滑らかさを測定するために、Chrome には最近改良が加えられており、デベロッパー ツールも組み込まれています。これらのツールをお試しいただき、アニメーションのベンチマークを行って、結果をお知らせください。

コメントは、件名に「[Smoothness Metrics]」と記入して Google グループ web-vitals-feedback に送信できます。皆様からのご意見、ご感想をお待ちしております