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