設計、實作、審查、除錯及逆向工程 UI 動態效果:CSS 轉場、關鍵影格、彈簧動畫、手勢、拖曳、緩動函數、時間控制、framer-motion,以及從螢幕錄影中提取動畫曲線。當被要求「加入動畫」、「讓這個感覺更流暢」、「審查我的動畫」、「加入滑動手勢」、「匹配這個緩動效果」、「逆向工程這個動畫」、「提取動畫曲線」,或「這個效果叫什麼…」來為模糊描述命名動態效果時使用。如需視覺方向請使用 ui-design;如需頁面層級的 UI 審查請使用 ui-audit。
UI 動畫
- 負責: 設計、實作、審查、除錯 UI 動態效果(彈簧、手勢、拖曳、緩動、CSS 轉場、關鍵影格、framer-motion),從錄影中測量動畫(提取影格、追蹤、擬合曲線)以產出程式碼及交接規格,以及為描述中的動態效果命名(反向查詢詞彙)。
- 不負責: 選擇整體視覺方向、色板或字型(請使用
ui-design),審查整個頁面的 UI 品質(請使用ui-audit),或命名文字效果規格(請使用外部animate-text技能,若已安裝)。
從錄影中逆向工程動畫的標準位置:將「逆向工程這個動畫」和「匹配這個緩動效果」導向此處,而非其他獨立技能。如果輸入是螢幕錄影或影片,你正在測量動畫:請遵循逆向工程工作流程。否則(設計、實作、審查)請使用下方規則與工作流程。
參考檔案
| 檔案 | 何時閱讀 |
|---|---|
| references/decision-framework.md | 預設:決定是否/為何要動畫,選擇緩動特性 |
| references/spring-animations.md | 彈簧物理、framer-motion useSpring、設定彈簧參數、Apple 阻尼/響應值、中斷機制 |
| references/component-patterns.md | 按鈕、彈出視窗、工具提示、抽屜、模態框、提示訊息搭配動畫 |
| references/clip-path-techniques.md | clip-path 用於揭示、分頁、長按刪除、比較滑桿 |
| references/gesture-drag.md | 拖曳、滑動關閉、動量、指標捕捉、速度傳遞、動量投影 |
| references/performance-deep-dive.md | 卡頓、CSS vs JS、WAAPI、CSS 變數陷阱、Framer Motion 注意事項 |
| references/review-format.md | 審查動畫程式碼:十項標準(每項附有立即觸發標記)、Before/After/Why 表格、Block/Approve 裁決 |
| references/contextual-animations.md | 情境式圖示切換、逐字間隔進場、固定偏移離場 |
| references/transition-recipes.md | 安裝 CSS 轉場:卡片縮放、徽章、下拉選單、模態框、面板、頁面滑動、圖示切換、數字彈入、文字切換、成功提示、頭像懸浮、錯誤抖動 |
| references/measurement-guide.md | 逆向工程:要測量什麼、肉眼 vs 腳本、讀取 metrics.json、選擇 ROI |
| references/curve-fitting.md | 逆向工程:讀取 fit_curves.py 輸出、彈簧 vs 貝茲曲線、判斷擬合誤差、非對稱開啟/關閉 |
| references/code-output.md | 逆向工程:產出 CSS、Motion/Framer Motion、SwiftUI、React Native、UIKit 程式碼 |
| references/choreography.md | 逆向工程:多元素/多階段動畫:交錯、移動前模糊、各邊緣穩定 |
| references/vocabulary.md | 為使用者模糊描述的動態效果命名(「這個效果叫什麼…」) |
核心規則
- 為回饋、方向感、連續性或刻意愉悅而動畫。如果只是「看起來很酷」且使用者經常看到,就不要做。
- 永遠不要為鍵盤觸發的動作(快捷鍵、方向鍵導航、Tab/焦點)加入動畫;這些動作會重複執行,動畫會讓它們感覺變慢。
- 對於可中斷的 UI,優先使用 CSS 轉場:關鍵影格在中斷時會從零重新開始,轉場則會重新定位。僅在預先確定的序列中使用關鍵影格。
- 實作優先順序:CSS 轉場 > WAAPI > CSS 關鍵影格 > JS(
requestAnimationFrame);在負載下 CSS 保持流暢,而 JS 會掉幀。 - 非對稱時間:偶爾發生的互動可以進場稍慢、離場快速。高頻率的短暫 UI(懸浮高亮、彈出視窗、面板切換)則反過來:進場立即(0ms),離場短暫淡出(100-150ms),讓動作感覺即時。
- 使用
@starting-style處理 DOM 進入;在不支援的情況下回退到data-mounted屬性。 - 小量的
filter: blur(2px)可以隱藏內容切換時粗糙的交叉淡出。
動態設計原則
- 連續性勝過瞬間移動。 在兩個狀態中都可見的元素應在原地轉場;從元素所在位置展開,而不是淡入一個新實例。永遠不要複製一個持續存在的元素,或在共享元件的視圖之間硬切;硬切會失去空間方向感。
- 方向性動畫符合位置。 分頁和輪播轉場應沿著與空間佈局匹配的方向動畫(左到右為前進,右到左為返回)。
- 從觸發點浮現。 覆蓋層、托盤和面板應從開啟它們的元素向外動畫;通用的畫面中央進場會破壞空間方向感。
- 成對狀態一起動畫。 如果開啟有動畫,關閉也要有。如果懸浮有動畫,焦點和按壓狀態也要有對等的回饋。不要只打磨重複互動的一半。
- 愉悅感與頻率成反比。 較少發生的互動可以有更多個性;高頻動作必須隱形。
- 動畫提升感知速度。 流暢的轉場感覺比硬切更快,即使載入時間相同。
要動畫什麼
- 移動:僅
transform和opacity;它們跳過佈局和繪製。 - 狀態回饋:
color、background-color和opacity是可接受的。 - 永遠不要動畫佈局屬性(
width、height、top、left);它們每幀都會觸發佈局重新計算。(例外:刻意調整容器大小的補間動畫,請參閱卡片縮放食譜。) - 永遠不要使用
transition: all;它會動畫非預期的屬性,並在未來默默加入新屬性。請明確列出它們。 - 避免在核心互動中使用
filter動畫;如果無法避免,保持模糊 ≤ 20px(重度模糊成本高,尤其在 Safari 中)。 - SVG:在
<g>包裝器上應用變換,並設定transform-box: fill-box; transform-origin: center;否則它們會圍繞畫布原點旋轉/縮放。 transform: scale()也會縮放子元素(圖示、文字、邊框會等比縮放),這與width/height不同:這是按壓回饋的一個特性,但當內部元素需要保持固定大小時要考慮到。- 在主題切換期間停用轉場(
[data-theme-switching] * { transition: none !important }),否則每個主題屬性都會同時動畫。
緩動預設值
| 元素 | 持續時間 | 緩動函數 |
|---|---|---|
| 按鈕按壓回饋 | 100-160ms | cubic-bezier(0.22, 1, 0.36, 1) |
| 工具提示、小型彈出視窗 | 125-200ms | ease-out 或進場曲線 |
| 下拉選單、選擇器 | 150-250ms | cubic-bezier(0.22, 1, 0.36, 1) |
| 模態框、抽屜 | 200-350ms | cubic-bezier(0.22, 1, 0.36, 1) |
| 在畫面上移動/滑動 | 200-300ms | cubic-bezier(0.25, 1, 0.5, 1) |
| 頁面轉場 | 250-400ms | 進場或移動曲線 |
| 簡單懸浮(顏色/透明度) | 200ms | ease |
| 插圖/行銷 | 最多 1000ms | 彈簧或自訂 |
將常規 UI 保持在 300ms 以下;根據距離調整持續時間(全螢幕滑動可以超過 300ms,6px 的工具提示位移保持在 150ms 以下)。
命名曲線
- 進場:
cubic-bezier(0.22, 1, 0.36, 1)用於進場和基於變換的懸浮 - 移動:
cubic-bezier(0.25, 1, 0.5, 1)用於滑動、抽屜、面板 - 抽屜(iOS 風格):
cubic-bezier(0.32, 0.72, 0, 1)
避免在 UI 中使用 ease-in:它開始緩慢,因此元素會落後於使用者的動作,感覺遲鈍。優先使用來自 easing.dev 的自訂曲線,而非內建的 ease/ease-out,因為後者的溫和加速度感覺柔和而非果斷。
轉場決策規則
首先匹配 UI 元素,然後從 references/transition-recipes.md 中選擇食譜:
| UI 模式 | 食譜 |
|---|---|
| 觸發 + 浮動點/計數 | 通知徽章 |
| 觸發 + 錨定表面 | 選單下拉 |
| 頁面頂部的居中表面 | 模態對話框 |
| 滑入現有容器的面板 | 面板揭示 |
| 列表 ↔ 詳細或精靈步驟 | 頁面並排滑動 |
| 元素尺寸變化 | 卡片縮放 |
| 原地更新文字 | 文字狀態切換 |
| 同一位置的兩個圖示 | 圖示切換 |
| 數字更新 | 數字彈入 |
| 確認/成功時刻 | 成功慶祝 |
| 水平堆疊中的懸浮項目 | 頭像群組懸浮 |
| 表單驗證錯誤 | 錯誤狀態抖動 |
偏好開銷較低的轉場(僅 CSS),除非設計需要 JS 編排。
空間與序列
- 彈出視窗的
transform-origin設在觸發點(模態框保持center),對話框/選單進場從scale(0.85-0.9)而非scale(0),以及 30-50ms 的交錯(總計低於 300ms,最重要的元素領先)。完整規則與程式碼請參閱 references/component-patterns.md 和 references/contextual-animations.md。 - 成對元素規則: 一起動畫的元素(模態框 + 遮罩、工具提示 + 箭頭、FAB + 標籤)必須共享相同的緩動和持續時間。時間不匹配通常是「感覺不對勁」的原因。
無障礙
- 每個動畫都需要一個
prefers-reduced-motion: reduce路徑:停用變換/關鍵影格動畫,保留即時狀態變更或僅透明度淡出。所有食譜都包含此保護。 - 將懸浮動畫限制在
@media (hover: hover) and (pointer: fine)之後,否則觸控裝置會在點擊時重播懸浮效果。Tailwind v4 的hover:工具會自動套用此規則;在那裡跳過手動查詢。 - 在直接操作期間,保持元素鎖定在指標上,不加入緩動;僅在釋放後加入緩動。
效能
- 使用
IntersectionObserver暫停螢幕外的循環動畫;它們即使在不可見時也會消耗 GPU。 - 僅在重度動畫期間切換
will-change,且僅用於transform/opacity;之後移除。每次提升都會消耗合成器記憶體;在許多元素上永久提升比不提升更糟。 - 不要透過容器上的 CSS 變數來動畫拖曳;每次更新都會重新計算所有子元素的樣式。直接在移動元素上設定
transform。 - Motion 的
x/y值是軸向移動和拖曳的預設值(它們繞過 React 重新渲染)。僅當一個擁有者需要組合多個變換函數或與非 Motion 程式碼互通時,才使用完整的transform字串。 - 請參閱 references/performance-deep-dive.md 了解 WAAPI、合成圖層以及 CSS vs JS 比較表。
反模式
上述未涵蓋的高訊號失敗:
- 在掛載時沒有使用者觸發就動畫:意外的動畫會造成困惑;使用者沒有做任何事來觸發它。
- 拖曳邊界處的硬停感覺像壞掉;應加入摩擦力/阻尼,使移動在越過邊界後逐漸減弱(請參閱手勢拖曳參考)。
- 同時動畫容器及其交錯的子元素:每個容器選擇一個進場方式。如果面板滑入,其內容應在到達時已經可見。
- 第一個工具提示打開後,後續工具提示的動畫:群組中的後續工具提示應立即打開,否則工具列會感覺遲鈍。
(變換擁有者衝突和快速連續關鍵影格失敗已在上述效能和核心規則中明確說明。)
工作流程
複製並追蹤:
動畫進度:
- [ ] 步驟 1:決定該互動是否應該動畫
- [ ] 步驟 2:選擇目的、緩動和持續時間
- [ ] 步驟 3:選擇實作方式
- [ ] 步驟 4:載入相關的元件或技術參考
- [ ] 步驟 5:驗證時間、中斷和裝置行為
- 回答 references/decision-framework.md 中的四個問題:要動畫嗎?目的?緩動?速度?
- 從上方緩動預設值表中選擇持續時間。
- 選擇實作方式:CSS 轉場 > WAAPI > 彈簧 > 關鍵影格 > JS。
- 載入你的元件或技術的參考。
- 審查時,套用 references/review-format.md 中的嚴格態度:根據十項標準衡量,輸出 Before/After/Why 表格,然後給出分層裁決,最終以 Block/Approve 決定結束。
驗證
為每個檢查提供證據(DevTools 觀察,而非「看起來沒問題」):
- 搜尋 diff 中的佈局屬性轉場(
width、height、top、left)和transition: all。 - 快速重新切換元件;確認轉場會重新定位而非從零重新開始。
- 在 DevTools 動畫面板中將速度放慢至 10%,以捕捉在全速下不可見的時間和
transform-origin問題。 - 模擬
prefers-reduced-motion: reduce(DevTools 渲染面板),並確認每個動畫都有減少動畫的路徑。 - 確認
will-change在動畫周圍切換,而非永久設定,且循環動畫在螢幕外暫停。 - 在真實裝置上測試觸控互動;模擬器會低估手勢和懸浮點擊問題。
- 隔天以全新視角再次審查;開發期間遺漏的瑕疵會變得明顯。
逆向工程工作流程
使用此分支從螢幕錄影中測量現有動畫,然後產出程式碼和交接規格來重現它。scripts/ 下的腳本是標準的確定性路徑;請執行它們,而不是重建它們的邏輯。
依賴項: ffmpeg 用於影格提取(brew install ffmpeg);Python 搭配 pip install opencv-python numpy scipy 用於追蹤和曲線擬合。優雅降級:僅有 ffmpeg 時可以提取影格並進行視覺推理;追蹤和擬合需要 Python 套件。
逆向工程進度:
- [ ] 步驟 1:提取影格 + 聯絡表(若開啟與關閉不同則每個方向各一份)
- [ ] 步驟 2:視覺檢查:識別元素、效果、階段
- [ ] 步驟 3:決定精確度(僅肉眼 vs 腳本)
- [ ] 步驟 4:追蹤運動並擬合曲線(若升級)
- [ ] 步驟 5:註解編排(延遲、不對稱)
- [ ] 步驟 6:為目標平台產出程式碼
- [ ] 步驟 7:對照錄影驗證
- 提取。 執行
python3 scripts/extract_frames.py <video> <outdir>。使用--start/--duration裁剪到僅轉場部分;如果互動同時有開啟和關閉,則裁剪兩個時間視窗,並為每個方向執行一次管線(它們幾乎從不對稱)。將--fps匹配到來源(使用ffprobe探測),永遠不要取樣高於來源幀率。先打開contact_sheet.png。 - 視覺檢查。 命名移動的元素、每個效果(平移、縮放通常各向異性、透明度、模糊、圓角、陰影、顏色)以及階段,注意哪個屬性領先和落後。使用
references/measurement-guide.md中的檢查清單。 - 決定精確度。 簡單淡出或線性滑動:從聯絡表中讀取時間,跳到步驟 5。彈性、彈簧或多屬性運動:升級到步驟 4(肉眼判斷彈簧不可靠)。
- 追蹤和擬合。 執行
python3 scripts/track_motion.py <outdir>以產生metrics.json(傳入--bbox X,Y,W,H以隔離一個元素),然後執行python3 scripts/fit_curves.py <outdir>/metrics.json以獲得彈簧參數、三次貝茲曲線和每個屬性的擬合誤差。傳入與提取時相同的--fps。閱讀references/curve-fitting.md以選擇模型;兩者誤差都高表示多階段運動(分割並擬合每個區段)。 - 註解。 載入
references/choreography.md。建立時間偏移表(每個屬性何時開始和穩定);領先/落後間隙和過度拉伸比任何單一曲線更能傳達感覺。 - 產出。 將擬合參數代入
references/code-output.md中目標平台的模板。將移動保持在transform/opacity上。當開啟和關閉不同時產出兩個轉場,加上整合的交接規格,以便無需影片即可實作。 - 驗證。 重新推導:播放產出的動畫,螢幕錄製它,再透過
extract_frames.py執行,並排比較聯絡表。放慢至 0.1 倍速以確認階段順序和過度拉伸是否保留。確認程式碼僅動畫transform、opacity和filter。
逆向工程陷阱:
fit_curves.py預設--fps 30:以 60 提取但以預設值擬合,會導致duration_ms加倍而擬合剛度降至四分之一。務必將提取的 fps 傳遞給擬合。- 取樣高於來源幀率會複製影格:以 60 提取的 24 fps GIF 會因
metrics.json中的平台期而膨脹擬合誤差。探測並匹配來源幀率。 - 螢幕錄影會掉幀,且 iOS/QuickTime 錄製是可變幀率;連續相同的行是重複影格,而非暫停。如果平台期佔主導,請以更穩定的幀率重新錄製。
- 將開啟和關閉作為獨立片段測量並報告兩條曲線;永遠不要擬合一條曲線然後反過來使用(請參閱
references/choreography.md)。將擬合error高於 0.08 視為可疑。
相關技能
ui-design:視覺方向、色板、字型;在調整動畫之前先確定視覺系統。ui-audit:頁面/功能層級的 UI 品質審查;其動畫發現會回到此處進行修正。- 可選的外部
animate-text技能(若已安裝):精選的命名文字效果(打字機、行揭示、交錯建構)附有精確的 JSON 規格。






