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

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

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

スクロールやアニメーション中にページが「途切れる」または「フリーズする」ことはよくあることです。このような操作はスムーズではありません。このような問題に対処するため、Chrome チームは、アニメーション検出のためのラボ ツールへのサポートの追加と、Chromium 内のレンダリング パイプラインの診断の継続的な改善に取り組んできました。

最新の進捗状況をお知らせし、具体的なツールのガイダンスを提供し、今後のアニメーションの滑らかさの指標に関するアイデアについて説明します。皆様からのフィードバックをお待ちしております。

この記事では、次の 3 つの主なトピックについて説明します。

  • アニメーションとアニメーション フレームについて簡単に説明します。
  • アニメーションの全体的なスムーズさを測定する方法に関する現在の考え方。
  • ラボ ツールですぐに活用できる実用的なヒントをいくつかご紹介します。

アニメーションとは

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

ただし、アニメーションの実装が不十分であったり、アニメーションを追加しすぎたりすると、エクスペリエンスが低下し、まったく楽しくなくなる可能性があります。誰もが、過度に「便利な」遷移効果が追加されたインターフェースを操作したことがあるでしょう。しかし、パフォーマンスが低いと、実際には操作しづらくなることがあります。そのため、一部のユーザーはモーションの抑制を好む場合があります。これはユーザーの設定であるため、尊重する必要があります。

アニメーションの仕組み

簡単にまとめると、レンダリング パイプラインは、次の順序付けられたステージで構成されています。

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

アニメーションを定義する方法は多数ありますが、基本的には次のいずれかの方法で動作します。

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

これらのステージは順序付けられているため、パイプラインの下流にあるプロパティの観点からアニメーションを定義することが重要です。プロセスの早い段階で更新を行うほど、費用が増加し、スムーズに進まない可能性が高くなります。(詳しくは、レンダリング パフォーマンスをご覧ください)。

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

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

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

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

一定の間隔で更新を表示するため、視覚的な更新はバッチ処理されます。多くのディスプレイは、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() がスケジュールできなくなる可能性があります。
  • 同様に、長いアイドル ブロックがないと、ブラウザは他の長時間実行タスク(長時間のガベージ コレクションやその他のバックグラウンド処理、推測処理など)をスケジュールできません。
  • ポーリングがオンとオフを切り替えられる場合、フレーム バジェットが超過したケースが検出されない可能性があります。
  • ブラウザが更新頻度を変動させている場合(電源や可視性のステータスなどによる場合)は、ポーリングで誤検出が発生します。
  • そして最も重要な点として、実際にはすべてのタイプのアニメーション アップデートをキャプチャするわけではありません。

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

メインスレッドが遅延すると、視覚的な更新が途切れ始めます。ジャンクだ!

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

上の動画は、長いタスクをメインスレッドに定期的に挿入するページを示しています。これらの長いタスクは、ページが特定の種類のビジュアル アップデートを提供する能力を完全に損ないます。左上の requestAnimationFrame() が報告する FPS が 0 に低下しているのは、このためです。

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

これは、メインスレッドで多くのフレームがドロップされているにもかかわらず、コンポーザ スレッドでスクロールのフレームが正常に配信されている例です。長いタスクが完了しても、メインスレッドのペイント更新によって視覚的な変化は発生しません。rAF ポーリングではフレーム ドロップが 0 になると示唆されますが、視覚的には、ユーザーは違いに気付くことはできません。

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

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

上記の例は、ストーリーに requestAnimationFrame() 以上の要素があることを示しています。

では、アニメーションの更新とアニメーション フレームはどのような場合に重要になるのでしょうか。以下に、現在検討している基準をいくつか示します。ご意見をお寄せいただければ幸いです。

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

メインスレッドとコンポーザ スレッドの更新

アニメーション フレームの更新はブール値ではありません。フレームを完全にドロップするか、完全に表示するかのどちらかしかできないわけではありません。アニメーション フレームが部分的に 表示される理由はいくつかあります。つまり、古いコンテンツ新しいビジュアル アップデートが同時に表示される場合があります。

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

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

一方、メインスレッドの更新が最終的に表示可能になるまでに、複数のフレーム期限を過ぎてしまう場合もあります。ブラウザは一部更新されますが、最新版ではない可能性があります。

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

ペイントの更新がない

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

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

フレーム レンダリングの機会ごとに、最新のビジュアル アップデートが実際に画面に表示された割合を追跡できます。多くのフレーム(または時間)にわたってこれを行う能力を測定することを、一般的にフレーム スループットと呼びます。

GPU が本当に遅い場合、ブラウザ(またはプラットフォーム)は視覚的な更新を試みるレートをスロットリングし始め、有効なフレームレートが低下することもあります。技術的には、ドロップされたフレーム更新の数を減らすことができますが、視覚的にはフレーム スループットが低下したように見えます。

ただし、フレーム スループットが低い場合でも、必ずしも悪いわけではありません。ページがほとんどアイドル状態にあり、アクティブなアニメーションがない場合は、低いフレームレートでも高いフレームレートと同様に視覚的に魅力的です(バッテリーも節約できます)。

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

アニメーションの検出

フレーム スループットの高さは、重要なアニメーションがある期間に特に重要です。アニメーションの種類によって、特定のスレッド(メイン、コンポジタ、ワーカー)からの視覚的な更新に依存するため、その視覚的な更新は、そのスレッドが期限内に更新を提供することに依存します。スレッドの更新に依存するアクティブなアニメーションがある場合、そのスレッドはスムーズさに影響すると見なされます。

アニメーションには、定義や検出が容易なものとそうでないものがあります。宣言型アニメーション(ユーザー入力ドリブンのアニメーション)は、アニメーション可能なスタイル プロパティの定期的な更新として実装される JavaScript ドリブンのアニメーションよりも、定義が明確です。

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

質 vs 量

最後に、アニメーションとアニメーション フレームの更新を検出しても、アニメーションの更新数しかキャプチャされず、品質はキャプチャされないため、これは全体像の一部にすぎません。

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

また、<canvas> を活用するゲーム(オフスクリーン キャンバスなどの手法を使用してフレームレートを安定させる場合もあります)は、アニメーション フレームに関して技術的には完全にスムーズであるにもかかわらず、高品質のゲームアセットをシーンに読み込めなかったり、レンダリング アーティファクトが発生したりすることがあります。

もちろん、サイトにアニメーションがまったくない場合もあります。

古い「建設中」の GIF

当時としてはかなりクールだったと思います。

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

フレームが部分的に表示されたフレームや、スムーズさに影響しない方法でフレームがドロップされたフレームがある可能性があるため、各フレームに完全性スコアまたはスムーズさスコアがあると見なすようになりました。

単一アニメーション フレームの状態を解釈する方法の範囲は、ベストケースからワーストケースの順に次のとおりです。

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

これらの状態をスコアに変換できます。このスコアを解釈する方法の一つは、ユーザーが検出する確率と見なすことです。1 つのフレーム落ちはほとんど目立ちませんが、連続して発生し、スムーズさに影響するフレーム落ちは目立ちます。

すべてをまとめる: フレーム ドロップ率の指標

各アニメーション フレームの状態を詳しく調べる必要がある場合もありますが、エクスペリエンスに「一目でわかる」スコアを簡単に割り当てることも便利です。

フレームは部分的に表示される場合があり、フレーム更新が完全にスキップされても実際にスムーズさに影響しない場合があるため、フレームのカウントではなく、ブラウザが重要なときに視覚的に完全な更新を提供できない程度に重点を置いています。

メンタルモデルは、次のように変化する必要があります。

  1. フレームレート:
  2. 不足しているアニメーション アップデートと重要なアニメーション アップデートを検出して、
  3. 特定の期間における減少率

重要な更新を待機している時間の割合が重要です。これは、ユーザーがウェブ コンテンツの滑らかさを実際に体験する自然な方法と一致していると考えています。これまでは、次の指標を初期セットとして使用してきました。

  • 平均ドロップ率: タイムライン全体のアイドル状態以外のすべてのアニメーション フレーム
  • フレーム ドロップの最悪の割合: 1 秒のスライディング ウィンドウで測定されます。
  • フレーム ドロップのパーセンテージの 95 パーセンタイル: 1 秒のスライディング ウィンドウで測定されます。

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

デベロッパー ツールで実際にお試しください。

パフォーマンス HUD

Chromium には、フラグ(chrome://flags/#show-performance-metrics-hud)の背後に隠された、すっきりとしたパフォーマンス HUD があります。この HUD では、Core Web Vitals などのライブスコアを確認できます。また、時間の経過に伴うフレーム落ち率に基づくアニメーションの滑らかさに関するいくつかの実験的な定義も確認できます。

パフォーマンス HUD

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

レンダリング設定で DevTools の [フレーム レンダリングの統計情報] を有効にすると、新しいアニメーション フレームのライブビューが表示されます。このビューでは、部分的な更新と完全にドロップされたフレームの更新を区別するために色分けされています。報告される fps は、完全に表示されたフレームのみです。

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

DevTools のパフォーマンス プロファイル レコーディングのフレーム ビューア

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

フレームビューアのフレームは、vsync 境界とより正確に位置合わせされ、ステータスに基づいて色分けされます。上記のニュアンスのすべてを完全に可視化することはまだできませんが、近い将来、さらに追加する予定です。

Chrome DevTools のフレーム ビューア

Chrome トレース

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

トレース イベントを使用すると、次のことを明確に判断できます。

  • 実行中のアニメーション(TrackerValidation という名前のイベントを使用)。
  • アニメーション フレームの正確なタイムラインを取得する(PipelineReporter という名前のイベントを使用)。
  • アニメーションの更新がぎくしゃくしている場合は、PipelineReporter イベント内のイベントの内訳を使用して、アニメーションの高速実行を妨げている原因を正確に特定します。
  • 入力ドリブンのアニメーションの場合は、視覚的な更新を取得するまでの時間を確認します(EventLatency という名前のイベントを使用)。

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

次のステップ

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

個々のアニメーション フレームデータに基づく包括的な指標の設計に関するアイデアについては、引き続き最新情報をお知らせします。

今後は、ラボだけでなく現場の実際のユーザーに対して、アニメーションの滑らかさをパフォーマンスに優れた方法で測定できる API を設計したいと考えています。最新情報もぜひご確認ください。

フィードバック

アニメーションの滑らかさを測定するための Chrome の最新の改善とデベロッパー ツールをご紹介します。これらのツールをお試しいただき、アニメーションをベンチマークして、結果をお知らせください。

コメントは、web-vitals-feedback Google グループに送信してください。件名には「[スムーズネス指標]」と記載してください。ご意見をお待ちしております。