CSS Scroll-driven Animationsの使い方【scroll・view・読了バー】
スクロール進捗バーや、要素が画面へ入るタイミングに合わせたアニメーションは、これまでscrollイベントやIntersectionObserverを使って作ることが多いUIでした。CSS Scroll-driven Animationsを使うと、スクロールの進行量をアニメーションのタイムラインとして扱えます。
この記事では、ページ上部の読了進捗バーにscroll()、カードの表示にview()を使います。未対応環境でも本文が消えないようにフォールバックを先に設計します。
時間ではなくスクロール量で動かす
通常のCSSアニメーションは、開始してから何秒経ったかで進みます。Scroll-driven Animationsでは、ページがどこまでスクロールされたか、要素が表示領域のどこまで入ったかを進行度として使います。
2種類のタイムライン
| 関数 | 基準 | 向いているUI |
|---|---|---|
scroll() | スクロールコンテナの進行量 | 読了バー、横移動、背景変化 |
view() | 対象要素が表示領域を通過する進行量 | カード表示、見出し強調、画像変化 |
完成コード
HTML
<div class="reading-progress" aria-hidden="true"></div>
<main class="article-shell">
<img class="nines-logo" src="https://kjnine.com/wp-content/themes/kjnines_theme/images/svg2/nines_logo.svg" alt="NINES">
<header class="hero">
<p class="eyebrow">SCROLL-DRIVEN ANIMATIONS</p>
<h1>スクロール量に連動する記事ページ</h1>
<p>上部の進捗バーと、画面に入るカードの動きをCSSだけで作ります。</p>
</header>
<section class="feature-list">
<article class="feature-card"><span>01</span><h2>scroll()</h2><p>スクロールコンテナの進行量を使います。</p></article>
<article class="feature-card"><span>02</span><h2>view()</h2><p>要素が表示領域へ入る進行量を使います。</p></article>
<article class="feature-card"><span>03</span><h2>animation-range</h2><p>動き始めと終わりの範囲を調整します。</p></article>
<article class="feature-card"><span>04</span><h2>Fallback</h2><p>未対応でも内容は常に読める状態にします。</p></article>
</section>
</main>CSS
* {
box-sizing: border-box;
}
html {
color: #111;
background: #f2f5f9;
font-family: system-ui, sans-serif;
}
body {
min-height: 220vh;
margin: 0;
}
.reading-progress {
position: fixed;
z-index: 10;
inset: 0 0 auto;
height: 5px;
background: #0057ff;
transform: scaleX(0);
transform-origin: left;
animation: reading-progress linear both;
animation-timeline: scroll(root block);
animation-range: 0% 100%;
}
@keyframes reading-progress {
to {
transform: scaleX(1);
}
}
.article-shell {
position: relative;
width: min(100% - 32px, 920px);
margin: 0 auto;
padding: 80px 0 160px;
}
.nines-logo {
position: absolute;
top: 32px;
right: 0;
width: 68px;
opacity: 0.14;
}
.hero {
min-height: 65vh;
display: grid;
align-content: center;
}
.eyebrow,
.feature-card span {
color: #0057ff;
font-size: 0.78rem;
font-weight: 800;
letter-spacing: 0.14em;
}
.hero h1 {
max-width: 760px;
font-size: clamp(2.4rem, 8vw, 5.5rem);
line-height: 1.05;
}
.feature-list {
display: grid;
gap: 28px;
}
.feature-card {
min-height: 300px;
display: grid;
align-content: center;
padding: clamp(28px, 6vw, 64px);
border-left: 5px solid #0057ff;
background: #fff;
box-shadow: 0 18px 50px rgb(20 35 65 / 8%);
}
@supports (animation-timeline: view()) {
.feature-card {
animation: card-in linear both;
animation-timeline: view();
animation-range: entry 15% cover 35%;
}
@keyframes card-in {
from {
opacity: 0;
transform: translateY(48px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}
@media (prefers-reduced-motion: reduce) {
.reading-progress,
.feature-card {
animation: none;
transform: none;
opacity: 1;
}
}JavaScript
// このデモはCSSだけで動きます。
// 対応確認が必要な場合はCSS.supports()を利用できます。
const supported = CSS.supports('animation-timeline: scroll()');
console.log({ scrollDrivenAnimations: supported });読了進捗バーを作る
固定した横線へtransform: scaleX(0)を指定し、キーフレームの終点でscaleX(1)にします。animation-timeline: scroll(root block)を指定すると、ルートの縦スクロール量に合わせてバーが伸びます。
カードが画面に入る動きを作る
カードへanimation-timeline: view()を指定すると、カード自身が表示領域を通過する進行量を使えます。animation-range: entry 15% cover 35%で、画面へ入り始めてから十分見える位置までをアニメーション範囲にしています。
animation-rangeを調整する
| 値の考え方 | 見え方 |
|---|---|
| entry付近から開始 | 要素が画面へ入るタイミングで動く |
| coverまでに完了 | 要素が見える途中で動きを終えられる |
| 範囲を短くする | 素早く変化する |
| 範囲を長くする | スクロールへゆっくり追従する |
フォールバックを先に作る
最も大切なのは、未対応環境でカードが透明のままにならないことです。サンプルでは通常状態を「表示済み」にし、@supports (animation-timeline: view())の中だけでアニメーションを追加しています。新機能を使えなくても内容は読めます。
IntersectionObserverとの使い分け
| 方法 | 向いていること |
|---|---|
| Scroll-driven Animations | スクロール進行度に連続して追従する視覚表現 |
| IntersectionObserver | 表示された瞬間に処理・通信・クラス変更を行う |
画像の遅延読み込みや分析イベントなど、JavaScriptの処理を発火したい場合はIntersectionObserverが適しています。見た目をスクロール量へ連動させるだけならCSSを検討できます。
パフォーマンスと使いやすさ
- transformとopacity中心の軽い動きから始める
- 多数の大きな要素を同時に動かさない
- スクロールを奪う演出や極端な視差を避ける
prefers-reduced-motionで動きを減らす- 低速端末とスマートフォンで実際にスクロールする
CodePen埋め込み用エリア
HTMLとCSSを貼り、ページを縦にスクロールしてください。次にanimation-rangeを変え、カードが動き始める位置と終了位置を比べます。
あわせて読みたい
- JavaScriptで要素が画面に入ったらアニメーションする方法
- CSSで作る効果的なちょっとしたアニメーション10選
- SVGの線を描画するアニメーションの作り方
- View Transition APIの使い方
参考資料
まとめ
CSS Scroll-driven Animationsを使うと、scroll()でページの進行量、view()で要素の表示進行量へアニメーションを連動できます。通常状態では内容を表示し、対応環境だけ動きを追加する設計にすると、安全に段階的な強化ができます。