1. 題目與適用場景
一個內容頁面需要頂部閱讀進度條,文章卡片進入滾動容器時還要淡入。團隊先用 scroll 事件監聽器和 requestAnimationFrame 實作,但在低階裝置上出現主執行緒抖動。請使用 CSS Scroll-driven Animations 重新設計,解釋 scroll progress 與 view progress 的差別,並說明瀏覽器不支援時的降級、reduced-motion 和真實驗證方法。
2. 面試官考察點
- 是否理解滾動驅動動畫把 CSS 動畫綁定到 scroll timeline 或 view timeline,而非預設時間軸。
- 是否能區分 scroll() 追蹤滾動容器進度與 view() 追蹤元素在最近滾動容器中的可見範圍。
- 是否處理命名時間軸、動畫範圍、overflow 條件和動畫簡寫覆蓋順序。
- 是否把相容性、效能、鍵盤閱讀、減少動效和無 JavaScript 降級納入方案。
3. 回答前需要釐清的問題
- 進度條跟隨整個文件,還是跟隨內部文章滾動容器?
- 卡片動畫應在進入視窗時播放一次,還是隨可見比例正向和反向變化?
- 目標瀏覽器是否允許使用 animation-timeline,舊瀏覽器需要什麼視覺和功能降級?
- reduced-motion 使用者是否仍要看到進度變化,以及動畫是否影響閱讀順序和焦點?
4. 30 秒回答框架
我會把閱讀進度條綁定到滾動容器的 scroll progress timeline,把卡片綁定到元素的 view progress timeline。scroll() 表示滾動位置從起點到終點的進度,view() 表示元素在最近滾動容器中進入和離開可視區域的範圍。動畫仍由 keyframes 定義,animation-timeline 決定進度來源;必須確保容器真的可滾動,並明確動畫範圍。舊瀏覽器保留靜態進度提示或使用節流後的輕量 JavaScript,不能讓核心內容依賴動畫。最後補上 supports、reduced-motion、鍵盤和低階裝置測試。
5. 分步驟深入解答
第一步:確認滾動時間軸的語意
scroll progress timeline 由滾動容器的滾動位置驅動,通常從起點的 0% 走到終點的 100%。view progress timeline 由目標元素相對於最近滾動容器的可見範圍驅動,適合卡片進入和離開視窗時同步動畫。兩者都建立在 CSS Animations 和 Web Animations API 的時間軸模型上。
第二步:實作閱讀進度條
先讓文章容器出現實際溢出,再命名滾動時間軸,把進度條的 animation-timeline 指向同名時間軸。不要只設定 animation-timeline 而忘記 keyframes;也不要把沒有滾動範圍的容器誤當成可用時間軸。
.article-scroller {
overflow-y: auto;
scroll-timeline-name: --article-scroll;
scroll-timeline-axis: block;
}
.reading-progress {
transform-origin: left center;
animation: grow-progress linear;
animation-timeline: --article-scroll;
}
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}第三步:實作卡片的 view timeline
卡片可以使用匿名 view(),由最近的祖先滾動容器追蹤它的可見範圍。需要更精確控制時,用 view-timeline-name 和 view-timeline-inset 命名時間軸並調整開始、結束邊界。動畫範圍應與設計稿中的可見區間一致,避免卡片剛碰到邊緣就完成全部動畫。
.card {
animation: reveal-card linear both;
animation-timeline: view(block 15% 15%);
}
@keyframes reveal-card {
entry 0% { opacity: 0; transform: translateY(1rem); }
cover 45% { opacity: 1; transform: translateY(0); }
exit 100% { opacity: 0.6; }
}第四步:處理動畫範圍與簡寫陷阱
命名時間軸和匿名函式都需要有正確的滾動容器關係;overflow、軸向和元素層級任一不匹配都可能讓時間軸沒有進度。設定 animation 簡寫後再設定 animation-timeline,因為簡寫會把未寫出的時間軸重設為預設值。對複雜進入、覆蓋和退出階段,使用 animation-range 或範圍關鍵影格表達意圖,並在目標瀏覽器實測。
第五步:設計降級與無障礙驗證
用 @supports 檢測 animation-timeline。增強規則不可用時,保留靜態進度條、正常內容順序和可讀對比度;若確需 JavaScript fallback,只在滾動事件中做節流後的低成本更新,避免重複執行布局。prefers-reduced-motion: reduce 下移除位移和連續動效,仍保留進度資訊和焦點順序。驗證鍵盤滾動、螢幕閱讀器、快速反向滾動、巢狀滾動容器和低階裝置幀率。
6. 高品質示範回答
我會把閱讀進度條綁定到文章滾動容器的 scroll progress timeline,把卡片綁定到最近滾動容器的 view progress timeline。前者表示滾動位置從 0% 到 100%,後者表示元素進入、覆蓋和離開可視區的範圍。實作時先確認 overflow 產生真實滾動距離,再設定 keyframes 和 animation-timeline,避免 animation 簡寫覆蓋時間軸。透過 @supports 提供靜態降級,只有必要時使用節流的 JavaScript;reduced-motion 下移除位移和連續動畫但保留內容、進度和焦點。最後用鍵盤、讀屏、巢狀滾動和低階裝置驗證。
7. 常見錯誤
- 把 scroll() 和 view() 當成同一種進度 → 卡片無法按可見範圍變化 → 先說明滾動位置與元素可見範圍的差異。
- 容器沒有溢出卻宣告 scroll timeline → 動畫沒有進度 → 檢查內容尺寸、overflow 和軸向。
- 用 animation 簡寫覆蓋了 animation-timeline → 時間軸回到預設時間軸 → 簡寫後重新宣告 animation-timeline。
- 只在現代瀏覽器顯示內容 → 舊瀏覽器功能缺失 → 保留靜態進度和正常閱讀順序。
- reduced-motion 只把 opacity 設為零 → 內容回饋消失 → 拿掉位移和連續動效,保留語意和可見狀態。
8. 追問及應對
追問一:scroll timeline 和 view timeline 何時分別使用?
整個滾動區域的閱讀進度適合 scroll timeline;單個元素進入、覆蓋和離開滾動容器的動畫適合 view timeline。選擇依據是進度來源,而不是是否使用了 JavaScript。
追問二:為什麼宣告了 animation-timeline 卻沒有動畫?
常見原因是容器沒有實際滾動範圍、軸向不匹配、時間軸名稱不在祖先關係中,或 animation 簡寫後來把時間軸重設。應從布局溢出、時間軸關係和宣告順序逐項排查。
追問三:舊瀏覽器如何降級?
保留靜態進度提示和完整內容;若產品確實需要動態進度,再用節流的 scroll 監聽器做輕量更新。降級不應改變閱讀順序、鍵盤存取和焦點行為。
追問四:如何兼顧 reduced-motion?
在 prefers-reduced-motion: reduce 下移除位移、縮放和長時長動畫,保留進度條的靜態或瞬時更新、內容順序與可讀回饋,並驗證快速滾動不會遺失資訊。