This article was translated from Japanese by AI and may contain inaccuracies. For the most accurate content, please refer to the original Japanese version.
雪のうさぎ冬の夜のイラスト

CSS だけで動くスクロールドリブンアニメーション

Google Chrome 115 で追加されたスクロールドリブンアニメーションを使うことで、今まで JavaScript を使わなれけば実装できなかったようなスクロールと連動するアニメーションを CSS だけで実装できるようになりました。スクロールの進行状況に応じてバーを伸縮させるようなアニメーションや、要素が画面内に入ったタイミングでアニメーションを開始するようなことが実装できます。

Google Chrome 115 で追加されたスクロールドリブンアニメーションを使うことで、今まで JavaScript を使わなれけば実装できなかったようなスクロールと連動するアニメーションを CSS だけで実装できるようになりました。

スクロールドリブンアニメーションを利用することで、例えばスクロールの進行状況を表すバーであったり、要素が画面内に出現したタイミングでアニメーションを開始するようなことが実装できます。

従来の CSS のアニメーションは、ページが読み込まれた後に時間の経過に応じてアニメーションが再生されていました。これはアニメーションタイムラインのデフォルトであるドキュメントタイムラインと呼ばれるものです。

新しい CSS プロパティである animation-timeline を使うことで、以下の 2 種類のタイムラインでアニメーションを再生できます。

  • スクロールの進行状況タイムライン
  • ビューの進行状況(ビューポートの中に要素が入っているかどうか)タイムライン

なお、animation-timeline は 2023 年現在 Google Chrome と Edge でのみ利用可能であることに注意してください。

"animation-timeline" | Can I use... Support tables for HTML5, CSS3, etc

スクロールの進行状況タイムライン

animation-timeline プロパティを使って、スクロールの進行状況を表すバーを実装してみます。

スクロールの進行状況タイムラインは、スクロール範囲内の位置をアニメーションのタイムライン上の割合に変換します。スクロールの進行状況は 0% から 100% までの範囲で表され、下にスクロールすることで進行状況が 100% に近づきます。

スクロールの進行状況タイムラインを使用するには、scroll() 関数を animation-timeline プロパティに指定します。

.bar {
  width: 0%;
  background-color: blue;
  animation: linear progress;
  animation-duration: auto;
  animation-timeline: scroll();
}
 
@keyframes progress {
  0% {
    width: 0%;
  }
 
  100% {
    width: 100%;
  }
}

これにより、以下のようにスクロール量に応じてバーが伸縮していることが確認できます。

完全なコード例は以下のレポジトリで確認できます。

スクローラーを指定する

スクローラーとは、スクロールの進行状況を表すバーの進行状況を計算するための要素のことです。デフォルトでは、要素に一番近い要素(nearest)となります。

以下の例では、画面全体をスクロールしてもバーが伸縮せずに、ボックス内でスクロールしたときにバーが伸縮していることが確認できます。

スクローラーは scroll() 関数の第 1 引数で指定できます。指定できる値は以下の 3 種類です。

  • nearest
  • root:画面全体
  • self:要素自身

先の例で scroll(root) を指定することで、画面全体をスクロールしたときにバーが伸縮するようになります。

.bar {
  animation: linear progress;
  animation-duration: auto;
  animation-timeline: scroll(root);
}

scroll(self) を指定することでその要素自身がアニメーションの進行状況を表すようになります。以下の例ではボックス自体に背景色が変更するアニメーションを指定しています。

.box {
  animation: linear change-color;
  animation-duration: auto;
  animation-timeline: scroll(self);
}
 
@keyframes change-color {
  0% {
    background-color: #60a5fa;
  }
 
  50% {
    background-color: #a855f7;
  }
 
  100% {
    background-color: #ec4899;
  }
}

スクロールの方向

scroll() 関数の第 2 引数では、スクロールの方向を指定できます。指定できる値は以下の 4 種類です。

  • block:文章が縦書きの場合は縦方向、横書きの場合は横方向(デフォルト)
  • inline:文章が縦書きの場合は横方向、横書きの場合は縦方向
  • y:縦方向
  • x:横方向

次の例では、scroll(self inline) を指定して、横方向にスクロールに対するアニメーションを指定しています。

ビューの進行状況タイムライン

ビューの進行状況タイムラインを使用することで、要素が画面内に入ったタイミングでフェードインするようなアニメーションを実装できます。要素が画面内に入ったかどうかの判定は、IntersectionObserver のものとよく似ています。

ビューの進行状況タイムラインを使用するには、view() 関数を animation-timeline プロパティに指定します。

.block {
  animation: fade-in linear both;
  animation-timeline: view();
  animation-range: cover 30% cover 50%;
}
 
@keyframes fade-in {
  from {
    opacity: 0;
  }
 
  to {
    opacity: 1;
  }
}

ビューの施行状況タイムラインのデフォルトの範囲(cover)は全範囲となっています。つまり、要素が画面内に入ったタイミングでアニメーションが開始され、要素が完全に画面外に出たタイミングでアニメーションが終了します。

このデフォルトの範囲を使用すると、要素が完全に画面外になるまで opacity の値が 1 にならないため、画面内に見えている間には要素がフェードインしきっていないように見えてしまいます。

animation-range プロパティを使用することで、アニメーションの範囲を指定できます。上記の例では、範囲が全範囲であることは変わらず、対象要素が画面内に入ったタイミングから 30% の位置でアニメーションが開始され、50% の位置でアニメーションが終了するようになります。

完全なコード例は以下のレポジトリで確認できます。

animation-range プロパティには、cover 以外にも以下タイムラインの範囲を指定できます。

  • contain
  • entry
  • exit
  • entry-crossing
  • exit-crossing

それぞれ範囲の意味を文章で説明するのは困難なのですが、幸いなことに以下のページで視覚的に確認ができます。

View Timeline Ranges Visualizer

ビューの進行状況タイムラインの方向

ビューの進行状況タイムラインの方向は、scroll() 関数と同じように view() 関数の引数で指定できます。指定できる値は以下の 4 種類です。

  • block:文章が縦書きの場合は縦方向、横書きの場合は横方向(デフォルト)
  • inline:文章が縦書きの場合は横方向、横書きの場合は縦方向
  • y:縦方向
  • x:横方向

要素が画面内にあるとみなされる境界のオフセット

view() 関数の引数で、要素が画面内にあるとみなされる境界のオフセットを指定できます(view() 関数は引数の順番を問いません)。

この値は 1 つまたは 2 つの値で指定でき、1 つの値はスクロールポートの銭湯から内側へのオフセット、2 つの値はスクロールポートの端から内側へのオフセットを表します。

view(10px 20px);
view(10px);
 
view(inset 10px 20px);
 
view(20% 30% block);

まとめ

  • 従来は時間経過によるドキュメントタイムラインのみでアニメーションを再生できたが、スクロールドリブンアニメーションを使うことでスクロールの進行状況やビューの進行状況に応じてアニメーションを再生できるようになった
  • スクロールドリブンアニメーションは animation-timeline プロパティを使って指定する
  • スクロールの進行状況タイムラインは scroll() 関数を使って指定する
  • ビューの進行状況タイムラインは view() 関数を使って指定する

参考