CSSスクロール駆動アニメーション:ゼロJSで視差スクロール、プログレスバー、リビール効果を実現
前端工程(更新: 2026年6月2日)
scrollイベントリスナーとの決別
従来のスクロールアニメーションはJavaScriptのscrollイベント監視に依存していました:
// ❌ 従来の方法:JSリスナー + 強制同期レイアウト
window.addEventListener('scroll', () => {
const progress = window.scrollY / (document.body.scrollHeight - window.innerHeight);
progressBar.style.width = `${progress * 100}%`;
// 各scrollでリフローが発生!
});
CSSスクロール駆動アニメーションはこれらを純粋なCSSで実現し、コンポジタースレッドで実行され、メインスレッドをブロックしません:
/* ✅ 純粋なCSS:読書プログレスバー */
.progress-bar {
width: 100%;
height: 3px;
background: blue;
transform-origin: left;
animation: progress linear;
animation-timeline: scroll();
}
@keyframes progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
scroll-timeline構文
匿名スクロールタイムライン
/* 最も近いスクロール可能な祖先のスクロールに基づく */
.animated {
animation-timeline: scroll();
}
/* 方向を指定 */
.animated-x {
animation-timeline: scroll(x); /* 水平スクロール */
}
.animated-y {
animation-timeline: scroll(y); /* 垂直スクロール */
}
名前付きスクロールタイムライン
/* スクロールコンテナにタイムライン名を宣言 */
.scroll-container {
scroll-timeline: --page-scroll y;
overflow-y: auto;
}
/* アニメーション要素で参照 */
.animated-element {
animation: fade-in linear;
animation-timeline: --page-scroll;
}
view-timeline構文
view-timelineは要素がビューポートに出入りする進行状況に基づきます:
.reveal-item {
view-timeline: --item-reveal;
animation: reveal linear both;
animation-timeline: --item-reveal;
}
@keyframes reveal {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
view-timeline-range:アニメーション範囲の正確な制御
.reveal-item {
view-timeline: --item-reveal;
animation: reveal linear both;
animation-timeline: --item-reveal;
/* 要素がビューポートに入る最初の30%でのみアニメーションを再生 */
animation-range: entry 0% entry 30%;
}
| range値 | 意味 |
|---|---|
entry |
要素がビューポートに入り始める |
exit |
要素がビューポートから出始める |
cover |
要素が完全にビューポート内にある |
contain |
要素が完全に入ってから出始めるまで |
entry-crossing |
要素がビューポート端を越えて入っている |
実践例1:視差スクロール
.parallax-container {
overflow-y: auto;
scroll-timeline: --parallax y;
height: 100vh;
}
.parallax-bg {
animation: parallax-move linear;
animation-timeline: --parallax;
}
@keyframes parallax-move {
from { transform: translateY(0); }
to { transform: translateY(-200px); }
}
/* 異なる深度の視差速度 */
.parallax-layer-1 { animation: parallax-move-slow linear; animation-timeline: --parallax; }
.parallax-layer-2 { animation: parallax-move-medium linear; animation-timeline: --parallax; }
.parallax-layer-3 { animation: parallax-move-fast linear; animation-timeline: --parallax; }
@keyframes parallax-move-slow { to { transform: translateY(-50px); } }
@keyframes parallax-move-medium { to { transform: translateY(-150px); } }
@keyframes parallax-move-fast { to { transform: translateY(-300px); } }
実践例2:読書プログレスバー
/* ページ上部の読書進捗インジケーター */
.reading-progress {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: linear-gradient(90deg, #3b82f6, #8b5cf6);
transform-origin: left;
z-index: 1000;
animation: reading-progress linear;
animation-timeline: scroll(root y);
}
@keyframes reading-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
実践例3:スクロールリビール(Scroll Reveal)
/* 下からフェードイン */
.reveal-up {
view-timeline: --reveal;
animation: reveal-up linear both;
animation-timeline: --reveal;
animation-range: entry 0% entry 40%;
}
@keyframes reveal-up {
from {
opacity: 0;
transform: translateY(80px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* 左からスライドイン */
.reveal-left {
view-timeline: --reveal;
animation: reveal-left linear both;
animation-timeline: --reveal;
animation-range: entry 0% entry 30%;
}
@keyframes reveal-left {
from {
opacity: 0;
transform: translateX(-60px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
スタガードディレイ
/* animation-delayでスタガード効果を実現 */
.stagger-item:nth-child(1) { animation-delay: 0ms; }
.stagger-item:nth-child(2) { animation-delay: 80ms; }
.stagger-item:nth-child(3) { animation-delay: 160ms; }
.stagger-item:nth-child(4) { animation-delay: 240ms; }
.stagger-item:nth-child(5) { animation-delay: 320ms; }
実践例4:水平スクロールエリア
.horizontal-scroll {
overflow-x: auto;
scroll-timeline: --hscroll x;
}
.card {
animation: card-appear linear both;
animation-timeline: --hscroll;
animation-range: entry 0% entry 20%;
}
@keyframes card-appear {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
実践例5:スティッキーヘッダーの縮小
.page {
scroll-timeline: --page y;
overflow-y: auto;
}
.sticky-header {
position: sticky;
top: 0;
animation: header-shrink linear both;
animation-timeline: --page;
animation-range: 0px 100px;
}
@keyframes header-shrink {
from {
padding: 24px;
font-size: 2rem;
}
to {
padding: 12px;
font-size: 1.25rem;
}
}
実践例6:画像スプライトシーケンスアニメーション
.sprite-container {
scroll-timeline: --sprite y;
overflow-y: auto;
}
.sprite-image {
animation: sprite-steps steps(24) both;
animation-timeline: --sprite;
}
@keyframes sprite-steps {
from { background-position: 0 0; }
to { background-position: -2400px 0; } /* 24フレーム × 100px幅 */
}
JS vs CSS スクロールアニメーション比較
| 次元 | JS(scrollイベント) | CSS scroll-timeline |
|---|---|---|
| パフォーマンス | メインスレッド、カクつきの可能性 | コンポジタースレッド、60fps |
| コード量 | 20-50行 | 5-10行 |
| 互換性 | 全ブラウザ | Chrome 115+, Firefox 125+ |
| 柔軟性 | 任意のロジック | 線形/区分アニメーション |
| デバッグ | console.log | DevTools Animationsパネル |
| メモリ | 手動でリスナークリーンアップ必要 | 自動管理 |
Polyfill戦略
// サポート検出
const supportsScrollTimeline = CSS.supports('animation-timeline', 'scroll()');
if (!supportsScrollTimeline) {
// IntersectionObserver + CSSクラス切り替えにフォールバック
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
entry.target.classList.toggle('in-view', entry.isIntersecting);
});
}, { threshold: 0.2 });
document.querySelectorAll('.reveal-item').forEach(el => {
observer.observe(el);
});
}
ブラウザ互換性
| ブラウザ | scroll-timeline | view-timeline | animation-range |
|---|---|---|---|
| Chrome 115+ | ✅ | ✅ | ✅ |
| Edge 115+ | ✅ | ✅ | ✅ |
| Firefox 125+ | ✅ | ✅ | ✅ |
| Safari 17.4+ | ❌ | ❌ | ❌ |
Safariは現在実装中です。機能検出 + IntersectionObserverフォールバックの使用を推奨します。
ベストプラクティス
- view-timelineを優先:ほとんどのスクロールリビール効果は
scroll-timelineよりもview-timelineの方がシンプル - animation-rangeの正確な制御:要素が見えないときにアニメーションが実行されないようにする
- will-changeとの併用:アニメーション要素に
will-change: transform, opacityを追加 - 同時アニメーション要素を制限:20以上の同時実行スクロールアニメーションはパフォーマンスに影響する可能性
- 機能検出フォールバック:Safariがサポートしていない場合はIntersectionObserverでフォールバック
まとめ
CSSスクロール駆動アニメーションは2026年のフロントエンドで最もエキサイティングな新機能の1つです。視差スクロール、読書プログレスバー、スクロールリビールなどのクラシックな効果をJSからCSSに戻し、パフォーマンスが大幅に向上します(コンポジタースレッドで実行され、メインスレッドをブロックしません)。Safariのサポートはまだ完全ではありませんが、IntersectionObserverフォールバックと組み合わせることで、今すぐ本番環境で使用できます。
ブラウザローカルツールを無料で試す →
#滚动驱动动画#CSS动画#scroll-timeline#视差滚动#无JS动画