一部のアニメーションの動作が遅い理由

最新のブラウザでは、transformopacity の 2 つの CSS プロパティを安価にアニメーション化できます。それ以外のものをアニメーション化すると、60 フレーム / 秒(FPS)の滑らかな動きを実現できない可能性が高くなります。この投稿では、その理由について説明します。

アニメーションのパフォーマンスとフレームレート

ウェブ上でアニメーションを作成する場合、60 FPS を目標とするのが一般的です。このフレームレートにより、アニメーションがスムーズに表示されます。ウェブでは、フレームは画面の更新と再描画に必要なすべての作業を行うのにかかる時間です。各フレームが 16.7 ミリ秒(1,000 ミリ秒 / 60 ≈ 16.7)以内に完了しない場合、ユーザーは遅延を感じます。

レンダリング パイプライン

ウェブページに何かを表示するために、ブラウザは次の順序で手順を実行する必要があります。

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

この 4 つのステップは、ブラウザのレンダリング パイプラインと呼ばれます。

読み込み済みのページでアニメーションを実行すると、これらの手順が再び実行されます。このプロセスは、アニメーションを可能にするために変更する必要があるステップから始まります。

前述のとおり、これらのステップは順次実行されます。たとえば、レイアウトを変更するものをアニメーション化する場合、ペイントとコンポジットのステップも再度実行する必要があります。そのため、レイアウトが変更されるものをアニメーション化する方が、コンポジットのみが変更されるものをアニメーション化するよりもコストがかかります。

レイアウト プロパティのアニメーション

レイアウトの変更では、変更の影響を受けるすべての要素のジオメトリ(位置とサイズ)を計算します。1 つの要素を変更すると、他の要素のジオメトリを再計算する必要が生じる場合があります。たとえば、<html> 要素の幅を変更すると、その子要素のいずれかに影響する可能性があります。要素のオーバーフローと相互の影響により、ツリーの下位の変更が、レイアウト計算を最上位まで遡って行う原因になることがあります。

表示要素のツリーが大きいほど、レイアウト計算の実行に時間がかかります。

ペイント プロパティのアニメーション化

ペイントは、要素を画面に描画する順序を決定するプロセスです。多くの場合、パイプライン内のすべてのタスクの中で最も実行時間が長くなります。

最新のブラウザでは、ペイントの大部分はソフトウェア ラスタライザーで行われます。アプリ内の要素がレイヤにグループ化されている方法によっては、変更された要素以外の要素も再描画する必要がある場合があります。

複合プロパティのアニメーション化

コンポジットとは、ページをレイヤに分割し、ページの表示方法に関する情報をピクセルに変換(ラスタライズ)し、レイヤを組み合わせてページを作成(コンポジット)するプロセスです。

そのため、opacity プロパティは、アニメーション化してもコストがかからないもののリストに含まれています。このプロパティが独自のレイヤにある限り、合成ステップで GPU によって変更を処理できます。Chromium ベースのブラウザと WebKit は、opacity で CSS トランジションまたはアニメーションが設定されている要素に対して新しいレイヤを作成します。

レイヤとは

アニメーションやトランジションの対象となるものを新しいレイヤに配置することで、ブラウザはそれらのアイテムのみを再描画すればよく、他のすべてを再描画する必要がなくなります。Photoshop のレイヤの概念をご存知の方もいらっしゃるかもしれません。レイヤには、まとめて移動できる要素が多数含まれています。ブラウザのレンダリング レイヤも、この考え方に似ています。

ブラウザは、新しいレイヤに含めるべき要素を適切に判断しますが、判断を誤った場合は、レイヤの作成を強制する方法があります。詳しくは、高パフォーマンスのアニメーションを作成する方法をご覧ください。ただし、新しいレイヤはメモリを使用するため、慎重に作成する必要があります。メモリが限られているデバイスでは、新しいレイヤを作成すると、解決しようとしている問題よりもパフォーマンスの問題が大きくなる可能性があります。また、各レイヤのテクスチャを GPU にアップロードする必要があります。そのため、CPU と GPU 間の帯域幅の制約に達する可能性があります。

CSS と JavaScript のパフォーマンス

アニメーションには CSS と JavaScript のどちらを使うのがパフォーマンスの面で優れているのか、疑問に思うかもしれません。

CSS ベースのアニメーションと Web Animations(API をサポートするブラウザの場合)は、通常、コンポジター スレッドと呼ばれるスレッドで処理されます。これは、スタイル設定、レイアウト、ペイント、JavaScript が実行されるブラウザのメインスレッドとは異なります。つまり、ブラウザがメインスレッドでコストの高いタスクを実行している場合でも、これらのアニメーションは中断されることなく継続できます。

この記事で説明したように、変換と不透明度に対するその他の変更も、多くの場合、コンポジタ スレッドで処理できます。

アニメーションがペイント、レイアウト、またはその両方をトリガーする場合、メインスレッドで作業を行う必要があります。これは CSS アニメーションと JavaScript アニメーションの両方に当てはまります。レイアウトやペイントのオーバーヘッドは、CSS や JavaScript の実行に関連する作業をはるかに上回る可能性が高く、この質問は無意味になります。