実践パターン
現場でよく使う 10 のレシピ集。コピペで動く形で。
1. ページ進捗バー
.reading-bar {
position: fixed; top: 0; left: 0;
height: 3px; width: 100%;
background: linear-gradient(to right, orange, red);
transform-origin: left;
transform: scaleX(0);
z-index: 1000;
animation: read linear both;
animation-timeline: scroll(root);
}
@keyframes read { to { transform: scaleX(1); } }
2. リビール(ふわっと現れる)
.reveal {
opacity: 0;
transform: translateY(40px);
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% cover 30%;
}
@keyframes reveal {
to { opacity: 1; transform: translateY(0); }
}
3. パララックス(背景)
.parallax-bg {
animation: parallax linear both;
animation-timeline: view();
animation-range: cover 0% cover 100%;
}
@keyframes parallax {
from { transform: translateY(-15%) scale(1.1); }
to { transform: translateY(15%) scale(1.1); }
}
4. ヒーローでズームアウト
.hero img {
animation: zoom-out linear both;
animation-timeline: view();
animation-range: cover 0% cover 100%;
}
@keyframes zoom-out {
from { transform: scale(1.2); }
to { transform: scale(1); }
}
5. ヘッダーが小さくなる
header {
position: sticky; top: 0;
height: 80px;
animation: shrink linear both;
animation-timeline: scroll(root);
animation-range: 0 200px;
}
@keyframes shrink {
to { height: 50px; backdrop-filter: blur(8px); }
}
6. 円形プログレス
<div class="ring" aria-hidden="true">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" stroke="#eee" fill="none" stroke-width="6"/>
<circle class="bar" cx="50" cy="50" r="45" stroke="orange" fill="none" stroke-width="6"
stroke-dasharray="282.7" stroke-dashoffset="282.7" transform="rotate(-90 50 50)"/>
</svg>
</div>
.ring {
position: fixed; bottom: 20px; right: 20px; width: 50px;
}
.bar {
animation: fill linear both;
animation-timeline: scroll(root);
}
@keyframes fill { to { stroke-dashoffset: 0; } }
7. 横スクロールギャラリー
<div class="hscroll">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
</div>
.hscroll {
display: flex; overflow-x: auto;
scroll-snap-type: x mandatory;
gap: 16px;
scroll-timeline: --gallery x;
}
.card {
flex: 0 0 80%;
scroll-snap-align: center;
view-timeline-name: --c;
view-timeline-axis: x;
animation: swap linear both;
animation-timeline: --c;
animation-range: cover 0% cover 100%;
}
@keyframes swap {
0% { opacity: 0.4; transform: scale(0.9); }
50% { opacity: 1; transform: scale(1); }
100% { opacity: 0.4; transform: scale(0.9); }
}
8. 章ごとのナビハイライト
body { timeline-scope: --s1, --s2, --s3; }
section.s1 { view-timeline-name: --s1; }
section.s2 { view-timeline-name: --s2; }
section.s3 { view-timeline-name: --s3; }
.toc a { color: #999; }
.toc a.t1 { animation: hi linear both; animation-timeline: --s1; animation-range: contain 0% contain 100%; }
.toc a.t2 { animation: hi linear both; animation-timeline: --s2; animation-range: contain 0% contain 100%; }
.toc a.t3 { animation: hi linear both; animation-timeline: --s3; animation-range: contain 0% contain 100%; }
@keyframes hi { to { color: orange; font-weight: 700; } }
9. テキストがスクロールでカラフルに
h1 {
background: linear-gradient(90deg, #f9c, #fc6, #6cf);
background-clip: text;
color: transparent;
background-size: 200% 100%;
animation: hue linear both;
animation-timeline: scroll(root);
}
@keyframes hue { to { background-position: -200% 0; } }
10. スクロールスナップ + ピン留め演出
.story {
height: 300vh; /* 余白を作って長く */
position: relative;
}
.sticky {
position: sticky;
top: 0;
height: 100vh;
display: grid;
place-items: center;
animation: scenes linear both;
animation-timeline: view();
animation-range: cover 0% cover 100%;
}
@keyframes scenes {
0% { background: #f9c; transform: translateY(0); }
33% { background: #fc6; transform: translateY(-10px); }
66% { background: #6cf; transform: translateY(0); }
100% { background: #cf6; transform: translateY(10px); }
}
応用: 時計の針
.hand {
animation: spin linear infinite;
animation-duration: 1s;
animation-timeline: scroll(root);
}
@keyframes spin {
to { transform: rotate(360deg); }
}
スクロールするたびに針がぐるぐる回る面白い演出。
応用: 数字カウント
@property --num {
syntax: "<integer>";
inherits: false;
initial-value: 0;
}
.counter {
--num: 0;
counter-reset: c var(--num);
animation: count linear both;
animation-timeline: view();
animation-range: entry 0% cover 30%;
}
.counter::after { content: counter(c); }
@keyframes count { to { --num: 100; } }
@property + counter() で滑らかに数字が増えていく表現。
失敗を防ぐ初期値
.reveal {
/* 範囲外(=画面外)でも見えてしまわないよう、初期値を明示 */
opacity: 0;
transform: translateY(40px);
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% cover 30%;
}
パターン化のコツ
- 「何を動かすか」(要素 / プロパティ)を決める
- 「どこのスクロールに連動するか」(scroll() / view())
- 「どの範囲で動くか」(animation-range)
- 初期 / 終端の値を fill-mode: both で固定
推奨ライブラリ
ネイティブで足りないとき:
・motion(軽量)
・GSAP ScrollTrigger(重量級・万能)
・Lenis(スムーズスクロール)
いずれもネイティブ scroll-driven と併用可能。