UX 動畫
在設計與撰寫互動時應用這些規則。首先檢視專案的動畫代幣(token):儲存庫中的 AGENTS.md、CLAUDE.md、代幣檔案與設計文件會覆蓋下列預設值。
永遠啟用的核心
- 預設決定:功能型 UI 通常落在 120–180ms 區間。專案代幣優先。
- 為空間連續性、狀態變更、輸入回饋或避免突兀的彈出而製作動畫。
- 優先使用 named
transform與opacity轉場。絕不要使用transition: all。 - 進入的表面應減速;退出的表面應加速。退出可以更短。
- 縮放進入的表面從接近
scale(0.96)開始,絕不從scale(0)開始。 - 保持動畫可在
prefers-reduced-motion: reduce下替換;保留理解性與鍵盤存取。 - 為拖曳捕獲指標,防護次要觸控點,並在實際硬體上測試手勢。
- 在撰寫動畫或觸碰常見覆蓋層之前,完整閱讀此檔案。僅在需要 clip paths、3D、WAAPI 或效能深入探討時才使用
REFERENCE.md。
1. 判斷是否值得使用動畫
將出現頻率作為限制條件:頻繁重複的動作應即時或近乎即時;偶爾出現的表面可以建立空間連續性;罕見的說明性時刻可以帶有更多個性。動畫必須用來釐清變更、確認輸入、保留位置或防止突兀的出現。如果沒有做到這些,就移除它。不要讓鍵盤快捷鍵等待裝飾性動畫。
2. 選擇持續時間
120–180ms 區間是預設決定:它刻意比 Material 3 公佈的 short3 (150ms)、short4 (200ms) 與標準轉場範圍 (250–400ms) 更緊湊。這表達了對慢速功能型 UI 的偏好,而非公佈的可及性或平台要求。
| 表面 | 起點 |
|---|---|
| 按壓回饋 | 100–140ms |
| 工具提示或小型彈出視窗 | 120–160ms |
| 選單或下拉式選單 | 140–180ms |
| Modal | 180ms |
| Sheet | 180ms;當移動大部分視窗時可達 250ms |
退出的動畫使用較短的端點。這些是預設值,不是產品代幣的覆蓋值。
3. 選擇 easing
進入的表面應以強調感到達並穩定(減速);退出應迅速清除(加速)。Material 3 明確地命名這些方向:
:root {
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
--ease-enter: cubic-bezier(0.05, 0.7, 0.1, 1); /* emphasized-decelerate */
--ease-exit: cubic-bezier(0.3, 0, 0.8, 0.15); /* emphasized-accelerate */
}
僅在時間性進度中使用 linear。對於會經過刻意中間點的狀態,使用 ease-in-out 曲線。避免僅根據名稱選擇曲線:檢查繪製的形狀與移動方向。
4. 保持渲染低耗
優先使用 transform 與 opacity,它們可以停留在合成器上。指定屬性名稱,並讓佈局脫離動畫範圍。避免對 top、left、width、height、margin 或 padding 進行動畫,除非佈局變更就是重點且成本可接受。對於自適應高度,包裝器可以將 grid-template-rows: 0fr 轉場到 1fr,內部包裝器使用 min-height: 0 並由 overflow: hidden 裁剪。這仍然執行佈局;請對其進行效能剖析,而不是將其視為僅限合成器的捷徑。延後、簡短且僅在效能剖析顯示有幫助時才使用 will-change;圖層會消耗資源。
5. 確保狀態正確
- 為可按壓的控制項提供可見回饋;使用現有的顏色、邊框或陰影狀態,或在合適時使用小幅度的
scale(0.98)。不要覆蓋定位用的transform。 - 保留可見的焦點、鍵盤啟動與停用行為。動畫不得延遲焦點放置、Escape 處理或語義狀態更新。
- 彈出視窗、選單與工具提示透過
transform-origin從錨點展開;置中的 Modal 從中心展開。在狀態變更時保持原點穩定。 - 偏好使用轉場來處理可重複觸發的狀態。對自包含事件使用關鍵影格。
- 使用
@media (hover: hover) and (pointer: fine)來限制僅在懸停時提供的功能。 - 對於進入的元素,在支援的地方使用
@starting-style,或使用一個掛載狀態,使初始樣式在轉場開始前可被觀察到。
6. 減少動畫意味著替代方案
prefers-reduced-motion 設定涵蓋動畫被移除、減少或替換的情況;大範圍平移與縮放可能觸發前庭系統。在仍能解釋狀態的地方,將移動與縮放替換為透明度與顏色變化。保持焦點、載入與可見性變更易於理解。不要僅因為使用者要求減少動畫就讓元素消失。在 JavaScript 驅動的動畫與 CSS 中都尊重此偏好,包括在頁面開啟時偏好變化的情況。不要依賴 transitionend 事件來完成必要的狀態變更:轉場可能被停用或取消。將覆蓋範圍限定在受影響的元件,而不是全域地強制所有動畫使用極短的持續時間。
7. 手勢
在拖曳開始時呼叫 setPointerCapture():後續的指標事件將繼續目標鎖定該捕獲元素,直到釋放。根據調整過的速度或距離規則來關閉;沒有通用的滑動常數,因此請在實際裝置上校準。使用阻尼來限制移動範圍,而不是硬邊界。一旦手勢擁有指標,就忽略其他觸控點。允許使用者反轉進行中的手勢。透過清除拖曳狀態來處理 pointercancel 與 lostpointercapture。為手勢軸選擇 touch-action,避免不必要地阻擋頁面滾動;提供按鈕或鍵盤等效操作。
8. 處理中斷與彈簧
快速開啟 / 關閉 / 重新開啟應穩定在最新請求的狀態。從當前視覺狀態重新定位目標,而不是從頭開始重播。在取代或卸載時取消陳舊的回呼、監聽器與動畫;舊的退出動畫不得移除新開啟的表面。根據元件的焦點生命週期,將隱藏或正在退出的內容排除在鍵盤導航之外。
當直接操作或連續性從中受益時使用彈簧,並尊重專案的函式庫。彈簧參數在不同引擎之間有所不同:不要盲目複製 stiffness 與 damping 值。在實際元件上調整穩定時間與 overshoot;避免在文字密集的表面上彈跳,並在減少動畫情況下停用移動。
9. 在宣告完成前驗證
以 4 倍慢速重播,檢查屬性是否同時完成並從正確的位置開始。快速切換以暴露卡頓。在 DevTools 中模擬減少動畫。將拖曳操作用到邊緣、取消它、加入第二個觸控點,並在實體裝置上測試。對於執行時期計算的動畫值,請查閱 REFERENCE.md。說明實際執行了哪些檢查,以及哪些需要瀏覽器或實體裝置;僅靠程式碼審查無法確定感知的平滑度。
審查輸出
在稽核程式碼時,以一個緊湊的表格報告,最嚴重的優先:
| 檔案:行數 | 現狀 | 變更為 | 原因 |
|---|---|---|---|
Menu.tsx:18 | transition: all 300ms | named 屬性與專案代幣 | 避免意外的佈局動畫 |
僅報告在程式碼中可見的發現。如果 diff 是乾淨的,請直接說明,而不是憑空捏造發現。
由 mad7droid 策劃。