題目與使用場景
目標是讓動畫進度跟隨滾動,同時避免每次滾動都執行 JavaScript 處理器。CSS 滾動驅動動畫透過 animation-timeline、scroll() 和 view() 提供滾動進度與視圖進度時間軸。實作仍需要降級方案、減少動態效果、版面邊界和明確的瀏覽器支援策略。
面試官考察什麼
- 是否區分滾動容器進度和元素可見進度。
- 是否把
animation-timeline放在animation簡寫之後。 - 是否優先選擇 transform 和 opacity,而不是觸發版面配置的屬性。
- 是否使用特性查詢或無動畫基線。
- 是否尊重
prefers-reduced-motion,並在無動畫時保持內容可讀。 - 是否測試長頁面、巢狀滾動容器、sticky 元素和行動端效能。
作答前的釐清問題
- 進度跟隨頁面、巢狀滾動容器,還是元素進入視口?
- 動畫是在傳達狀態,還是純裝飾?
- 支援哪些瀏覽器和內嵌 WebView?
- 減少動態效果的使用者應該看到什麼?
- 是否能降級成靜態進度條或靜態展示?
- 是否有 sticky、transform 或虛擬清單祖先改變包含塊?
30 秒回答框架
「頁面進度使用滾動進度時間軸,卡片進入視口使用視圖進度時間軸。我先提供可讀的靜態基線,再在特性查詢中增強,並把 animation-timeline 放在動畫簡寫之後。動畫優先使用 transform 或 opacity,測試巢狀滾動和 sticky 元素,在 prefers-reduced-motion 下關閉非必要動畫。上線前驗證支援情況、影格成本、鍵盤存取和內容理解。」
深入作答步驟
步驟 1:選擇時間軸。
動畫映射到容器滾動範圍時使用滾動進度時間軸;動畫映射到元素穿過滾動視口的過程時使用視圖進度時間軸。
步驟 2:建立基線。
先以有用的靜態狀態渲染進度條和卡片。瀏覽器忽略動畫時,內容順序、對比度、控制項和狀態仍應可用。
步驟 3:加入 CSS 增強。
@supports (animation-timeline: scroll()) {
.progress {
animation: grow-progress linear both;
animation-timeline: scroll(root block);
}
.card {
animation: reveal linear both;
animation-timeline: view(block);
animation-range: entry 0% cover 35%;
}
}由於簡寫會重設時間軸相關子屬性,animation-timeline 要放在簡寫之後。巢狀滾動容器可能讓最近滾動祖先不明確,應明確命名或指定目標容器。
步驟 4:保持動畫屬性輕量。
優先使用 opacity 和 transform。長清單預設不要動畫 width、height、top、left 或大面積陰影,除非效能分析證明成本可接受。不要猜測性地給每張卡片加入 will-change。
步驟 5:處理減少動態效果。
@media (prefers-reduced-motion: reduce) {
.progress,
.card {
animation: none;
transform: none;
opacity: 1;
}
}沒有動畫時也要保留內容和進度含義。靜態指示器或瞬時狀態比只縮短長過渡更安全。
步驟 6:測試幾何關係與支援。
測試根滾動器、橫向和縱向巢狀滾動、sticky 元素、動態內容、縮放、鍵盤導覽和行動端視口變化。特性偵測只控制增強,不應隱藏基線。
步驟 7:測量並觀察。
用 DevTools 效能追蹤檢查樣式、版面配置、繪製和合成器工作。測試長清單和低階行動裝置,觀察時間軸不支援或中斷時內容是否仍可讀。
高品質示範回答
「頁面進度條使用 scroll(root block),因為它對應根滾動範圍;每張卡片使用 view(block) 和 entry 到 cover 的範圍,因為展示應跟隨自身可見度。卡片先正常渲染,再在 @supports 中加入時間軸,並把宣告放在動畫簡寫之後。動畫只使用 transform 和 opacity;減少動態效果使用者不做非必要動畫,並在巢狀場景明確滾動容器。最後測試鍵盤、sticky、動態內容、不支援的瀏覽器和行動端長清單效能。」
常見錯誤
- 每幀監聽 scroll → 處理器會和滾動競爭 → 關係可宣告時優先 CSS 時間軸。
- 混淆
scroll()與view()→ 動畫開始時機錯誤 → 按容器進度或元素可見度選時間軸。 - 把
animation-timeline放在簡寫前 → 簡寫會重設它 → 把時間軸宣告放在後面。 - 預設動畫版面屬性 → 長頁面可能產生昂貴工作 → 先用 transform/opacity,再分析效能。
- 不處理減少動態效果 → 使用者無法控制偏好 → 提供靜態狀態。
- 動畫不支援就隱藏內容 → 舊瀏覽器失去資訊 → 先建立基線。
- 假設最近滾動容器總是正確 → 巢狀容器會改變時間軸 → 指定並測試目標容器。
追問與回答
追問 1:scroll() 和 view() 有什麼區別?
scroll() 跟蹤滾動容器進度;view() 跟蹤元素穿過該容器滾動視口時的可見進度。
追問 2:為什麼時間軸必須放在簡寫後?
animation 簡寫會重設時間軸相關子屬性,提前宣告可能被清除。
追問 3:降級方案是什麼?
讓內容以最終可讀狀態顯示,使用靜態進度指示器;只有確有產品需要時才加入小型 JavaScript 增強。
追問 4:如何支援減少動態效果?
關閉非必要動畫、移除 transform,並用靜態樣式保留同樣的內容和狀態含義。
追問 5:可以動畫 height 嗎?
只有效能分析確認必要時才做。transform 和 opacity 通常避免版面配置工作,height 可能讓兄弟元素重新配置。
追問 6:巢狀滾動容器怎麼辦?
最近的適用滾動容器可能不是頁面。要測試幾何關係,並命名目標時間軸或指定正確容器參數。
追問 7:如何在生產中驗證?
記錄支援情況,在代表性裝置上做效能追蹤、減少動態效果檢查和無障礙驗證,並保留可用的靜態基線。