最新のブラウザでは、transform
と opacity
の 2 つの CSS プロパティを低コストでアニメーション化できます。他のものをアニメーション化すると、60 フレーム / 秒(FPS)の滑らかなアニメーションを実現できない可能性があります。この投稿では、その理由について説明します。
アニメーションのパフォーマンスとフレームレート
ウェブ上でアニメーションを作成する場合、フレームレートは 60 FPS が目標とされています。このフレームレートにより、アニメーションが滑らかに表示されます。 ウェブでは、フレームとは、画面の更新と再描画に必要なすべての処理を行うのにかかる時間です。各フレームが 16.7 ms(1,000 ms ÷ 60 ≈ 16.7)以内に完了しない場合、ユーザーは遅延を感じます。
レンダリング パイプライン
ウェブページに何かを表示するには、ブラウザが次の手順を順番に行う必要があります。
- スタイル: 要素に適用されるスタイルを計算します。
- レイアウト: 各要素のジオメトリと位置を生成します。
- ペイント: 各要素のピクセルをレイヤに塗りつぶします。
- 合成: レイヤを画面に描画します。
この 4 つのステップは、ブラウザのレンダリング パイプラインと呼ばれます。
すでに読み込まれたページの内容をアニメーション化する場合は、この手順を再度行う必要があります。このプロセスは、アニメーションを実行するために変更する必要があるステップから始まります。
前述のとおり、これらの手順は順番に行う必要があります。たとえば、レイアウトを変更するアニメーションを作成した場合は、ペイントと合成のステップも再度実行する必要があります。そのため、レイアウトを変更するアニメーションは、コンポジションを変更するアニメーションよりもコストが高くなります。
レイアウト プロパティのアニメーション化
レイアウトの変更では、変更の影響を受けるすべての要素のジオメトリ(位置とサイズ)を計算します。1 つの要素を変更すると、他の要素のジオメトリの再計算が必要になる場合があります。たとえば、<html>
要素の幅を変更すると、その子要素のいずれかが影響を受ける可能性があります。要素がオーバーフローして互いに影響し合う方法により、ツリーのさらに下位で変更を行うと、レイアウト計算が一番上まで戻ることがあります。
表示される要素のツリーが大きいほど、レイアウト計算にかかる時間は長くなります。
ペイント プロパティのアニメーション化
ペイントとは、要素を画面にペイントする順序を決定するプロセスです。通常、ペイントはパイプライン内のすべてのタスクの中で最も実行時間が長いため、
最新のブラウザでのペイントの大半は、ソフトウェア ラスタライザで行われます。アプリ内の要素がレイヤにグループ化される方法によっては、変更された要素以外の要素もペイントする必要がある場合があります。
複合プロパティのアニメーション化
コンポジットとは、ページをレイヤに分割し、ページの外観に関する情報をピクセルに変換(ラスタライズ)し、レイヤを組み合わせてページを作成する(合成)プロセスです。
そのため、opacity
プロパティは、アニメーション化に費用がかからないもののリストに含まれています。このプロパティが独自のレイヤにある限り、その変更は合成ステップ中に GPU によって処理されます。Chromium ベースのブラウザと WebKit では、opacity
に CSS 遷移またはアニメーションがある要素に対して新しいレイヤが作成されます。
レイヤとは
アニメーション化または遷移する要素を新しいレイヤに配置することで、ブラウザは他のすべての要素ではなく、そのアイテムのみを再描画する必要があります。Photoshop のレイヤの概念はご存じでしょう。レイヤには、一緒に移動できる要素が多数含まれています。ブラウザのレンダリング レイヤは、その考え方に似ています。
ブラウザは、新しいレイヤに配置する要素を適切に判断しますが、要素が漏れた場合は、レイヤの強制作成を行う方法があります。詳しくは、高パフォーマンスのアニメーションを作成する方法をご覧ください。ただし、各レイヤはメモリを使用するため、新しいレイヤを作成する際は注意が必要です。メモリが限られているデバイスでは、新しいレイヤを作成すると、解決しようとしている問題よりもパフォーマンスの問題が発生する可能性があります。また、各レイヤのテクスチャを GPU にアップロードする必要があります。そのため、CPU と GPU 間の帯域幅の制約に達する可能性があります。
CSS と JavaScript のパフォーマンス比較
アニメーションに CSS と JavaScript のどちらを使用するのがパフォーマンスの観点から優れているか、疑問に思うかもしれません。
CSS ベースのアニメーションと ウェブ アニメーション(API をサポートするブラウザ)は、通常、コンポジタ スレッドと呼ばれるスレッドで処理されます。これは、スタイリング、レイアウト、描画処理、JavaScript が実行されるブラウザのメイン スレッドとは異なります。つまり、ブラウザがメインスレッド上で負荷の高いタスクをいくつか実行している場合でも、これらのアニメーションは中断されることなく続行できます。
この記事で説明したように、形状と不透明度に対するその他の変更も、多くの場合はコンポジタ スレッドで処理されます。
アニメーションがペイント、レイアウト、またはその両方をトリガーする場合は、メインスレッドで処理する必要があります。これは、CSS アニメーションと JavaScript アニメーションの両方に当てはまります。レイアウトやペイントのオーバーヘッドは、CSS や JavaScript の実行に関連する作業を大幅に減らし、質問の答えを予想できません。