高性能な CSS アニメーションを作成する方法

このガイドでは、高パフォーマンスの CSS アニメーションを作成する方法について説明します。

推奨事項の背景にある理論については、一部のアニメーションが遅い理由をご覧ください。

ブラウザの互換性

このガイドで推奨するすべての CSS プロパティは、クロスブラウザを適切にサポートしています。

要素を移動する

要素を移動するには、transform プロパティの translate または rotation キーワード値を使用します。

たとえば、アイテムをビュー内にスライドするには、translate を使用します。

.animate {
  animation: slide-in 0.7s both;
}

@keyframes slide-in {
  0% {
    transform: translateY(-1000px);
  }
  100% {
    transform: translateY(0);
  }
}

以下の 360 度の例では、アイテムを回転することもできます。

.animate {
  animation: rotate 0.7s ease-in-out both;
}

@keyframes rotate {
  0% {
    transform: rotate(0);
  }
  100% {
    transform: rotate(360deg);
  }
}

要素のサイズを変更する

要素のサイズを変更するには、transform プロパティの scale キーワード値を使用します。

.animate {
  animation: scale 1.5s both;
}

@keyframes scale {
  50% {
    transform: scale(0.5);
  }
  100% {
    transform: scale(1);
  }
}

要素の公開設定を変更する

要素の表示と非表示を切り替えるには、opacity を使用します。

.animate {
  animation: opacity 2.5s both;
}

@keyframes opacity {
  0% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

レイアウトまたはペイントをトリガーするプロパティを避ける

アニメーションに CSS プロパティ(transformopacity 以外)を使用する前に、プロパティがレンダリング パイプラインに与える影響を確認してください。どうしても必要な場合を除き、レイアウトやペイントをトリガーするプロパティは使用しないでください。

レイヤ作成の強制

一部のアニメーションが遅い理由で説明したように、新しいレイヤに要素を配置することで、レイアウトの残りの部分を再ペイントすることなく再ペイントできます。

多くの場合、ブラウザは新しいレイヤに配置するアイテムを適切に判断しますが、will-change プロパティを使用して手動でレイヤを強制的に作成することもできます。このプロパティは、名前が示すように、この要素がなんらかの形で変更されることをブラウザに伝えます。

CSS では、このプロパティを任意のセレクタに適用できます。

body > .sidebar {
  will-change: transform;
}

ただし、仕様では、常に変更される可能性のある要素にのみ、このアプローチを採用することが推奨されています。上記の例が、ユーザーがスライドイン / スライドアウトできるサイドバーだった場合、該当する可能性があります。ページ上の項目の中には頻繁に変更されないものもあるため、変更が発生する可能性がある時点で JavaScript を使用して will-change を適用することをおすすめします。ブラウザが必要な最適化を行うのに十分な時間を確保してから、変更が停止したらプロパティを削除する必要があります。

will-change をサポートしていないまれなブラウザ(現時点では Internet Explorer と思われます)でレイヤ作成を強制する必要がある場合、transform: translateZ(0) を設定できます。

遅いアニメーションやジャンクのあるアニメーションをデバッグする

Chrome DevTools と Firefox DevTools には、アニメーションが遅い理由や動作がぎくしゃくする原因を解明するためのさまざまなツールが用意されています。

アニメーションがレイアウトをトリガーするかどうかを確認する

transform 以外を使用して要素を移動するアニメーションは、動作が遅くなる可能性があります。次の例では、topleft をアニメーション化し、transform を使用した場合、同じ視覚的な結果が得られます。

すべきでないこと
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     top: calc(90vh - 160px);
     left: calc(90vw - 200px);
  }
}
推奨事項
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     transform: translate(calc(90vw - 200px), calc(90vh - 160px));
  }
}

次の 2 つの Glitch の例でこれをテストし、DevTools を使用してパフォーマンスを確認できます。

Chrome DevTools

  1. [パフォーマンス] パネルを開きます。
  2. アニメーションの実行中に、ランタイム パフォーマンスを記録します。
  3. [概要] タブを調べます。

[Summary] タブの [Rendering] にゼロ以外の値が表示される場合は、アニメーションが原因でブラウザがレイアウト処理を行っている可能性があります。

[Summary] パネルには、レンダリングに 37 ms、ペイントに 79 ms と表示されます。
animation-with-top-leftの例では、レンダリング処理が発生します。
[Summary] パネルに、レンダリングとペイントの値が 0 と表示されます。
animation-with-transform サンプルでは、レンダリング処理は行われません。

Firefox DevTools

Firefox DevTools では、ウォーターフォールを使用して、ブラウザがどこで処理に時間がかかっているかを把握できます。

  1. [パフォーマンス] パネルを開きます。
  2. [Start Recording Performance] パネルで、アニメーションの再生中に [Start Recording Performance] を選択します。
  3. 記録を停止し、[Waterfall] タブを調べます。

[Recalculate Style] のエントリがある場合、ブラウザはレンダリング ウォーターフォールの開始位置から開始する必要があります。

アニメーションでフレームがドロップされているかどうかを確認する

  1. Chrome DevTools の [レンダリング] タブを開きます。
  2. [FPS メーター] チェックボックスをオンにします。
  3. アニメーションの実行時、値を確認します。

FPS メーター UI の上部に [Frames] というラベルが表示されます。その下に、50% 1 (938 m) dropped of 1878 の行に沿った値が表示されます。高パフォーマンスのアニメーションは、高い割合(99% など)になります。割合が高い場合は、ドロップされるフレームが少なく、アニメーションが滑らかに見えることを意味します。

FPS メーターでフレームの 50% がドロップされたと表示される
animation-with-top-leftの例では、フレームの 50% がドロップされます
FPS メーターで 1% のフレームしかドロップされない
animation-with-transform の例では、フレームの 1% のみがドロップされます。

アニメーションがペイントをトリガーするかどうかを確認する

ペイントに関しては、ある物事が他の物よりも高価です。たとえば、ぼかしなど(シャドウなど)は、赤いボックスを描画するよりもペイントに時間がかかります。ただし、CSS に関しては、必ずしも明らかではありません。background: red;box-shadow: 0, 4px, 4px, rgba(0,0,0,0.5); は、必ずしもパフォーマンス特性が大きく異なるように見えるとは限りませんが、実際には大きく異なります。

ブラウザの DevTools を使用すると、再ペイントが必要な領域や、ペイントに関連するパフォーマンスの問題を特定できます。

Chrome DevTools

  1. Chrome DevTools の [レンダリング] タブを開きます。
  2. [ペイント フラッシュ] を選択します。
  3. 画面上でポインタを移動します。
再描画されることを示すため緑色でハイライト表示された UI 要素
Google マップのこの例には、再ペイントされる要素が示されています。

画面全体が点滅している場合や、変更されるはずのない領域がハイライト表示されている場合は、調査できます。

ペイントによるパフォーマンスの問題の原因となっているプロパティを特定する必要がある場合は、Chrome DevTools のペイント プロファイラが役立ちます。

Firefox DevTools

  1. [設定] を開き、[ペイント フラッシュを切り替える] のツールボックス ボタンを追加します。
  2. 検査するページでボタンをオンに切り替えて、マウスを動かすかスクロールして、ハイライト表示された領域を確認します。

おわりに

可能であれば、アニメーションを opacitytransform に制限して、レンダリング パスの合成ステージでアニメーションを維持します。DevTools を使用して、パスのどのステージがアニメーションの影響を受けるかを確認します。

ペイント プロファイラを使用して、特にコストの高いペイント オペレーションがあるかどうかを確認します。何か見つけたら、別の CSS プロパティを使用すると、デザインを変えてもパフォーマンスが向上するかどうかを確認します。

will-change プロパティは、パフォーマンスの問題が発生した場合にのみ、慎重に使用してください。