CSS Scroll-Driven Animations 完整教學:捲動動畫的新時代來了
還記得以前要做捲動動畫,非得靠 JavaScript 監聽 scroll 事件,然後用 IntersectionObserver 或是 requestAnimationFrame 來處理嗎?那段日子終於要過去了。CSS Scroll-Driven Animations 讓我們可以純用 CSS 就做出流暢的捲動連動動畫,而且效能比 JS 方案好上不少。
為什麼要用 Scroll-Driven Animations?
先說結論:效能。傳統的 JavaScript scroll 監聽跑在主執行緒上,稍微複雜一點的動畫就容易掉幀。而 CSS 動畫可以交給合成器執行緒處理,完全不阻塞主執行緒。
除了效能之外,程式碼量也差很多。以前可能要寫個幾十行 JS 加上 CSS,現在幾行 CSS 就搞定。我自己在幾個專案裡實測過,改用 Scroll-Driven Animations 之後,捲動時的 FPS 穩定維持在 60,體感差異很明顯。
scroll() 與 animation-timeline 基礎
Scroll-Driven Animations 的核心概念是「時間軸」。傳統的 CSS 動畫用時間(秒、毫秒)來驅動,而捲動動畫用捲動進度來驅動。有兩種時間軸:
- scroll():綁定到某個捲動容器的整體捲動進度
- view():綁定到元素進入/離開可視區域的進度
先來看最基本的 scroll() 用法:
@keyframes progress-bar {
from { width: 0%; }
to { width: 100%; }
}
.reading-progress {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: linear-gradient(to right, #3b82f6, #8b5cf6);
animation: progress-bar linear;
animation-timeline: scroll();
}就這樣,一個閱讀進度條就完成了。animation-timeline: scroll() 告訴瀏覽器這個動畫要跟著頁面捲動走,而不是跟著時間走。注意我們不需要設定 animation-duration,因為進度完全由捲動位置決定。
view() 時間軸:元素進場動畫
view() 是我個人覺得最實用的功能。它讓你可以在元素捲動進入畫面時觸發動畫,完全取代了以前 IntersectionObserver 的工作:
@keyframes reveal {
from {
opacity: 0;
transform: translateY(60px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}關鍵在 animation-range 這個屬性。entry 0% 代表元素剛進入可視區域的底部邊緣,entry 100% 代表元素完全進入可視區域。你可以調整這個範圍來控制動畫的觸發時機,彈性非常大。
如果你已經熟悉 CSS Container Queries 教學 中的響應式概念,搭配 Scroll-Driven Animations 可以做出更精緻的互動體驗。
實作視差滾動效果
視差滾動(Parallax)一直是很受歡迎的效果,但以前要做得好蠻費工的。現在用 CSS 就能輕鬆實現:
@keyframes parallax-move {
from { transform: translateY(-80px); }
to { transform: translateY(80px); }
}
.hero-background {
animation: parallax-move linear;
animation-timeline: scroll();
}
.hero-foreground {
animation: parallax-move linear;
animation-timeline: scroll();
animation-range: 0% 50%;
}透過讓前景和背景以不同速率移動,就能營造出深度感。重點是 animation-range 可以限制動畫只在特定的捲動範圍內執行,不用擔心捲到底部時元素跑太遠。
具名時間軸與進階控制
當頁面上有多個捲動容器時,你需要用具名時間軸來指定要綁定哪一個:
.scroll-container {
overflow-y: auto;
scroll-timeline-name: --card-timeline;
scroll-timeline-axis: y;
}
.animated-element {
animation: slide-in ease-out;
animation-timeline: --card-timeline;
}用 scroll-timeline-name 自訂名稱,然後在子元素的 animation-timeline 裡引用。這在做橫向捲動的 gallery 或是卡片輪播時特別好用。
三個實戰案例
案例一:捲動時縮小的 Header
@keyframes shrink-header {
from {
padding-block: 2rem;
font-size: 1.5rem;
}
to {
padding-block: 0.5rem;
font-size: 1rem;
}
}
.site-header {
position: sticky;
top: 0;
animation: shrink-header linear forwards;
animation-timeline: scroll();
animation-range: 0px 200px;
}案例二:圖片捲動揭露
@keyframes image-reveal {
from { clip-path: inset(0 100% 0 0); }
to { clip-path: inset(0 0 0 0); }
}
.gallery-image {
animation: image-reveal ease-out both;
animation-timeline: view();
animation-range: entry 10% entry 90%;
}案例三:數字跳動計數器
@keyframes count-up {
from { --num: 0; }
to { --num: 100; }
}
@property --num {
syntax: "";
initial-value: 0;
inherits: false;
}
.counter {
animation: count-up linear;
animation-timeline: view();
animation-range: entry 0% cover 50%;
counter-reset: num var(--num);
}
.counter::after {
content: counter(num);
} 這個計數器的技巧用到了 @property 來讓自訂屬性可以被動畫化,再搭配 CSS counter 顯示數字,整個過程零 JavaScript。
瀏覽器支援與降級策略
截至 2026 年,Chrome、Edge、Firefox 都已經完整支援 Scroll-Driven Animations。Safari 從 17.4 開始也加入了支援,不過部分進階功能可能還有差異。
建議用 @supports 來做漸進增強:
@supports (animation-timeline: scroll()) {
.card {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
}
/* 不支援的瀏覽器直接顯示 */
.card {
opacity: 1;
}如果你想讓版面配置也具備同樣的靈活度,可以看看 CSS Grid 完整教學,Grid 搭配 Scroll-Driven Animations 可以做出很驚豔的排版效果。
效能注意事項
雖然 CSS 動畫本身效能很好,但有幾點還是要注意:
- 盡量只動畫
transform和opacity,避免觸發 layout 重排 will-change不要亂加,用在真正需要的元素上就好- 大量元素同時動畫時,考慮用
content-visibility: auto來延遲渲染 - 測試時開啟 DevTools 的 Performance 面板,確認沒有長任務
Scroll-Driven Animations 絕對是 2026 年前端開發者該掌握的技能。它不只是語法糖,而是從根本上改變了我們處理捲動互動的方式。趁現在瀏覽器支援已經到位,趕快把手上專案裡的 JS 捲動監聽換掉吧。
繼續閱讀
CSS View Transitions API 完整教學:用原生 CSS 打造絲滑頁面過渡動畫
相關文章
你可能也喜歡
探索其他領域的精選好文