本文へ移動

k8o

お問い合わせ

255文字以内で入力してください

GitHub Issueからのお問い合わせもお待ちしております。

Blog

公開: 2026年11月7日(土)約6分で読めます

Scroll-driven animationsでスクロールに合わせてCSSのアニメーションを進める

Scroll-driven animationsは、時間ではなくスクロールの位置や要素の見え方でCSSのアニメーションを進める機能です。Chrome 115とSafari 26に続いてFirefox 159が対応し、Baseline 2026に加わりました。scroll()とview()の2つのタイムラインと、animation-rangeで進む区間を決める書き方を整理します。

Newly availableScroll-driven animations2026年〜
  • Chrome115
  • Edge115
  • Firefox159
  • Safari26

はじめに

読み進めた割合をバーで示したり、画面に入ってきたカードを少しずつ表示したりしたいことがあります。これまではscrollイベントやIntersectionObserverで状態を受け取り、JavaScriptからスタイルを書き換えていました。

Scroll-driven animationsは、CSSのアニメーションを時間ではなくスクロールで進める機能です。スクロールの位置で進めるscroll()と、要素の見え方で進めるview()の2つを使い分けます。Chrome 115とSafari 26に続いてFirefox 159が対応し、Baseline 2026に加わりました。

scroll()でスクロールの位置に合わせる

animation-timeline: scroll()を書くと、アニメーションはスクロールコンテナのスクロール位置で進みます。このタイムラインは、先頭にいるときが0%、末尾までスクロールしたときが100%です。

css
@keyframes grow {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

.progress {
  position: sticky;
  top: 0;
  transform-origin: 0 50%;
  animation: grow auto linear;
  animation-timeline: scroll();
}

animation-timelineは、先に書くと後のanimationショートハンドに初期値で上書きされるので、animationより後に書きます。仕様の草案ではanimationの中にscroll()も書けますが、Chrome 156、Safari 27.2、Firefox 159のどれでも宣言ごと無効になります。animationの時間に書いたautoは、タイムライン全体を使って進めるための値です。

scroll()の引数には、見るスクロールコンテナと軸を書きます。スクロールコンテナはnearest(最も近い祖先。既定値)・root(ビューポート)・self(要素自身)から、軸はblock(既定値)・inline・x・yから選びます。

Playground

スクロール量をプログレスバーで示す

scroll-driven-animations/playgrounds/scroll-driven-animations

animation-timeline: scroll()で、スクロールコンテナを読み進めた割合に合わせてプログレスバーを伸ばします。

段落 1:スクロールすると、上のバーが読み進めた割合だけ伸びます。

段落 2:スクロールすると、上のバーが読み進めた割合だけ伸びます。

段落 3:スクロールすると、上のバーが読み進めた割合だけ伸びます。

段落 4:スクロールすると、上のバーが読み進めた割合だけ伸びます。

段落 5:スクロールすると、上のバーが読み進めた割合だけ伸びます。

段落 6:スクロールすると、上のバーが読み進めた割合だけ伸びます。

段落 7:スクロールすると、上のバーが読み進めた割合だけ伸びます。

段落 8:スクロールすると、上のバーが読み進めた割合だけ伸びます。

段落 9:スクロールすると、上のバーが読み進めた割合だけ伸びます。

段落 10:スクロールすると、上のバーが読み進めた割合だけ伸びます。

段落 11:スクロールすると、上のバーが読み進めた割合だけ伸びます。

段落 12:スクロールすると、上のバーが読み進めた割合だけ伸びます。

スクロールコンテナの中身があふれずスクロールできないときは、タイムラインが働かず、キーフレームの値が当たりません。プログレスバーの例では、ページが短いと最初から伸びきった状態で表示されます。

view()で要素の見え方に合わせる

scroll()は、スクロールコンテナ全体のうちどこまでスクロールしたかを見ます。これに対してanimation-timeline: view()は、アニメーションする要素自身がスクロールコンテナの見えている領域(スクロールポート)のどこにあるかを見ます。このタイムラインは、要素が末尾側の端から見え始めたときが0%、先頭側の端から出きったときが100%です。ページのどこに置いた要素でも、スクロールポートを横切っている間だけタイムラインが進みます。

view()の引数には、scroll()と同じ軸と、見えていると判定する範囲を内側へ狭めるinsetを書けます。

css
@keyframes reveal {
  from {
    clip-path: inset(0 100% 0 0);
  }
  to {
    clip-path: inset(0);
  }
}

.card {
  animation: reveal auto linear both;
  animation-timeline: view();
  animation-range: entry;
}

animation-rangeを書かないと、アニメーションは見え始めから出きるまでをかけて進むので、スクロールポートの真ん中に来たカードはまだ左半分しか見えません。animation-rangeはアニメーションを進める区間を決めるプロパティで、上の例のようにentryを書くと、入りきった時点で全体が見えます。区間には次の4つの名前を使えます。

  • cover:見え始めてから出きるまでの全体(既定値のnormalと同じ)
  • contain:要素全体がスクロールポートに収まっている間
  • entry:入り始めてから入りきるまで
  • exit:出始めてから出きるまで

animation-fill-modeをbothにしておくと、区間の前では最初の、後では最後のキーフレームの値が保たれます。animation-rangeもanimation-timelineと同じく、先に書くと後のanimationで上書きされるので、animationより後に書きます。こちらは仕様でもanimationの中には書けません。

Playground

animation-rangeを切り替えて進む区間を比べる

scroll-driven-animations/playgrounds/scroll-driven-animations

animation-timeline: view()でカードを左から少しずつ表示し、animation-rangeを切り替えてカードが現れる区間を比べられます。

カードのanimation-range

カード 1

カード 2

カード 3

デモでは、カードのanimation-rangeを切り替えられます。coverではカードがスクロールポートの真ん中に来ても左半分しか見えず、entryでは入りきった時点で全体が見えます。

名前を付けて離れた要素を動かす

scroll()は祖先のスクロールコンテナを、view()は要素自身を見ます。それ以外の要素に合わせて動かしたいときは、タイムラインに名前を付けます。スクロールコンテナにはscroll-timelineで、見え方を追う要素にはview-timelineで、--から始まる名前を宣言します。動かす要素のanimation-timelineには、その名前を書きます。

仕様では、名前を文書全体から参照できます。Firefox 159は仕様どおり隣の要素からも参照できましたが、Chrome 156とSafari 27.2では宣言した要素とその子孫からしか参照できませんでした。祖先ではない要素から参照するときは、共通の祖先にtimeline-scopeで名前を書きます。

css
.layout {
  timeline-scope: --panel;
}

.layout > .panel {
  overflow-y: auto;
  scroll-timeline: --panel;
}

.layout > .indicator {
  animation: grow auto linear both;
  animation-timeline: --panel;
}

Chrome 156とSafari 27.2のtimeline-scopeは、子孫が宣言した名前を、その要素の中のどこからでも参照できるようにします。仕様のtimeline-scopeは逆に、参照できる範囲をその要素の中に限ります。どちらの動きでも、.layoutの中のインジケーターはパネルのタイムラインを参照できます。

おわりに

Scroll-driven animationsで、スクロールに合わせたアニメーションをCSSだけで書けるようになりました。スクロールの位置で進めるならscroll()、要素の見え方で進めるならview()を使い、進む区間はanimation-rangeで絞ります。Web Animations APIでも、ScrollTimelineかViewTimelineをelement.animate()のtimelineに渡せば同じことができます。

読了率 0%
もくじ