📋 目錄
過去十年,每當你需要「隨著滾動執行動畫」時,幾乎一定需要 JavaScript:監聵 scroll 事件、計算元素位置、触發 CSS 類切換。IntersectionObserver 緩解了部分壓力,但對於「進度條跟著滾動走」這類簡單需求,仍然需要湋雜的計算。CSS Scroll-Driven Animations 改變了這一切——現在,你只需要一行 CSS。
核心概念:時間線不再是時間,是滾動
傳統 CSS Animation:
/* 傳統動畫:由「經過的時間」驅動 */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.element {
animation: fadeIn 1s ease-out;
}
Scroll-Driven Animation:
/* 滾動驅動動畫:由「滾動進度」驅動 */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.element {
animation: fadeIn linear;
animation-timeline: scroll(); /* 滾動進度取代時間 */
}
當使用者滾動頁面時,animation-timeline: scroll() 讓動畫的進度直接映射到滾動位置——不需要 JavaScript,不需要 scroll 事件監聽。
三種時間線設定方式
方式一:animation-timeline: scroll()
最簡單的形式。用 scroll() 函式指定哪個滾動軸(block、inline、x、y):
.progress-bar {
animation: grow linear;
animation-timeline: scroll(); /* 預設:nearest 祖先的 block 軸 */
}
/* 明確指定 */
.progress-bar {
animation-timeline: scroll(y); /* 垂直滾動 */
}
scroll() 的四種參數:
| 參數 | 作用 |
|---|---|
scroll() | 最近的祖先滾動容器的 block 軸 |
scroll(block) | block 軸(預設,垂直) |
scroll(inline) | inline 軸(水平) |
scroll(x) | 明確 X 軸 |
scroll(y) | 明確 Y 軸 |
方式二:命名時間線(推薦)
用命名方式連接 scroll container 和動畫元素:
/* 1. 命名時間線 */
.scroller {
scroll-timeline-name: --progress-timeline;
/* 或寫成屬性:*/
/* scroll-timeline: --progress-timeline block; */
}
/* 2. 元素使用這個時間線 */
.progress-bar {
animation: grow linear;
animation-timeline: --progress-timeline; /* 引用 scroller 的時間線 */
}
好處:可以明確指定「哪個滾動區塊控制哪個動畫」,避免意外的祖父元素時間線。
方式三:view():視圖時間線(最强大)
view() 讓元素的動畫由「自己在視口中的可見程度」來控制:
.reveal-element {
animation: slideIn linear;
animation-timeline: view(); /* 當元素進入視口,動畫從 0% 到 100% */
}
實作一:進度條
最經典的用例——一個 <progress> 元素,跟著整頁滾動從 0% 長到 100%:
<style>
@keyframes progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.scroll-progress {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: #3b82f6;
transform-origin: left center;
animation: progress linear;
animation-timeline: scroll(); /* 跟著整頁滾動 */
}
</style>
<div class="scroll-progress"></div>
十行 CSS,不需要 JavaScript。
實作二:視圖時間線揭示效果
當元素進入視口時,淡入並稍微向上移動——過去需要 IntersectionObserver:
<style>
@keyframes reveal {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card {
animation: reveal linear;
animation-timeline: view(); /* 由視圖中的位置決定進度 */
/* 也可以設定範圍:*/
/* animation-range: entry 0%; entry 25%; */
}
/* 也可以設定只在進入時有效:*/
.card-enter-only {
animation-range: entry 0%; entry 25%;
}
</style>
<section>
<div class="card">卡片 1</div>
<div class="card">卡片 2</div>
<div class="card">卡片 3</div>
</section>
animation-range 讓你精確控制「動畫在哪個進度區間播放」:
entry 0%:元素完全離開視口底部entry 100%:元素完全進入視口頂部cover 0% 100%:元素完整穿越視口的整個過程
實作三:Sticky Header 樣式變化
當 Header 變成 Sticky 時,改變背景和字體大小:
<style>
@keyframes headerStyle {
from {
background: transparent;
padding: 1.5rem 2rem;
}
to {
background: rgba(255, 255, 255, 0.95);
padding: 0.75rem 2rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
}
.site-header {
position: sticky;
top: 0;
z-index: 100;
animation: headerStyle linear;
animation-timeline: view(); /* 當 header 在視口中滑過時 */
animation-range: exit 0% exit 20%;
}
</style>
<header class="site-header">
<nav>...</nav>
</header>
animation-range:精確控制播放時機
| 範圍值 | 意義 |
|---|---|
normal | 預設 0% 到 100% |
entry 0% | 元素剛進入視口 |
entry 100% | 元素完全進入視口 |
exit 0% | 元素剛離開視口 |
exit 100% | 元素完全離開視口 |
cover 0% 100% | 元素穿越整個視口的全過程 |
contain 0% 100% | 元素在視口中的整段時間 |
/* 動畫只在元素完整處於視口中時播放 */
.fade-while-visible {
animation: fade linear;
animation-timeline: view();
animation-range: contain 0% contain 100%;
}
與 JavaScript 方案的比較
| 維度 | Scroll-Driven Animations | IntersectionObserver | JS Scroll Listener |
|---|---|---|---|
| JavaScript | 不需要 | 需要 | 需要 |
| 性能 | 原生 compositor thread | 觀測性,較好 | 最差(每次 scroll 都觸發) |
| 精確度 | 依賴瀏覽器實現 | 良好 | 完全控制 |
| 支援觸發次數 | 每幀一次 | 每次 entrance/exit | 每次 scroll |
| 瀏覽器支援 | 全部主流(2024+) | 全部主流 | 全部 |
瀏覽器支援
截至 2026 年,所有主流瀏覽器都完整支援 Scroll-Driven Animations:
| 瀏覽器 | 支援版本 |
|---|---|
| Chrome | 115+(2023年8月) |
| Edge | 115+(與 Chrome 同步) |
| Firefox | 123+(2024年3月) |
| Safari | 17.4+(2024年3月) |
可以用以下方式檢測支援:
if (CSS.supports('animation-timeline', 'scroll()')) {
// 使用 Scroll-Driven Animations
document.documentElement.style.animationTimeline = 'scroll()';
} else {
// 回退到 IntersectionObserver 或 JS
}
總結:什麼時候用?
用 Scroll-Driven Animations:
- 進度條、可視化進度指示
- 視圖進入/離開揭示效果(替代 IntersectionObserver)
- Sticky 區塊的樣式動畫
- 讀取進度指示器
- 簡單的 parallax 效果
仍然需要 JavaScript:
- 需要根據滾動位置做非線性的計算(如複雜的 parallax 深度)
- 需要操作 DOM 的實際屬性(不只是 transform/opacity)
- 需要與使用者輸入同步(如拖曳)
CSS Scroll-Driven Animations 不是要取代所有的滾動 JavaScript,而是讓「常見的滾動動畫模式」用純 CSS 實現——這佔了過去 80% 的使用場景。
延伸閱讀
- CSS Container Queries 實戰入門 — 另一個強大的 CSS 新特性
本文基於 MDN CSS Scroll-Driven Animations 整理,已獲所有主流瀏覽器支援(Chrome 115+、Firefox 123+、Safari 17.4+)。