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

最新のブラウザでは、2 つの CSS プロパティ(transformopacity)を低コストでアニメーション化できます。それ以外をアニメーション化した場合、60 フレーム/秒(FPS)という非常に滑らかな速度を達成できない可能性があります。この投稿では、その理由について説明しています。

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

ウェブ上で何かをアニメーション化する場合、60 FPS のフレームレートを目標にすることが広く受け入れられています。このフレームレートにより、アニメーションが滑らかに表示されます。ウェブにおいて、フレームとは画面の更新と再ペイントに必要なすべての作業を行うのに要する時間です。各フレームが 16.7 ミリ秒(1,000 ミリ秒 / 60 約 16.7)以内に完了しない場合、ユーザーは遅延を感じます。

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

ウェブページに何かを表示するには、ブラウザが次の手順を踏む必要があります。

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

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

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

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

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

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

可視要素のツリーが大きいほど、レイアウト計算の実行にかかる時間が長くなります。

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

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

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

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

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

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

レイヤとは

アニメーション化する要素や遷移する要素を新しいレイヤに配置することで、ブラウザはそれらのアイテムを再描画するだけでよく、それ以外の要素はすべて再描画する必要がなくなります。Photoshop のレイヤという概念はおなじみかもしれません。レイヤには多数の要素が含まれており、それらは一緒に移動できます。ブラウザ レンダリング レイヤも、考え方とよく似ています。

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

CSS と JavaScript のパフォーマンスの比較

パフォーマンスの観点から、アニメーションに CSS と JavaScript のどちらを使用した方がよいのか、疑問に思われるかもしれません。

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

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

アニメーションがペイント、レイアウト、またはその両方をトリガーする場合、メインスレッドが処理を実行する必要があります。これは CSS と JavaScript の両方のアニメーションに当てはまります。また、レイアウトやペイントのオーバーヘッドにより、CSS や JavaScript の実行に関連する作業が軽視され、疑問が解消する可能性が高くなります。