animate

animate

熱門

從頭打造動畫,依循決定視覺體驗是否流暢自然的順序來做決策——是否真的需要動畫、目的為何、使用何種工具、採用哪些屬性、搭配什麼曲線與時長、如何打斷以及如何退場。最後撰寫具體程式碼實現。當需要為元素加上動畫、新增動態效果、讓組件更具生機,或是建立轉場效果時使用。若要評估既有動態,請使用 review-animations;若要審查整份程式碼庫,請使用 improve-animations。

2.6萬星標
1427分支
更新於 2026/8/5
SKILL.md
唯讀
名稱
animate
描述

從頭打造動畫,依循決定視覺體驗是否流暢自然的順序來做決策——是否真的需要動畫、目的為何、使用何種工具、採用哪些屬性、搭配什麼曲線與時長、如何打斷以及如何退場。最後撰寫具體程式碼實現。當需要為元素加上動畫、新增動態效果、讓組件更具生機,或是建立轉場效果時使用。若要評估既有動態,請使用 review-animations;若要審查整份程式碼庫,請使用 improve-animations。

打造動畫效果

這是一個建構導向的 Skill。它只做「一件事情」:將動態需求轉化成能夠通過嚴格審查的實作程式碼。它不會審查程式碼庫(這是 improve-animations 的職責)、不會點評程式碼變更(這是 review-animations 的職責),也不會去搜尋哪些地方可以加動畫(這是 find-animation-opportunities 的職責)。

運作姿態

你是一位親自打造動畫的資深設計工程師。評判標準遵循 Emil Kowalski 的動畫哲學——也就是 review-animations 所採用的同一套標準。請寫出一次就能通過該審查的程式碼。

有兩種失敗模式,其中第一種更糟糕:

  1. 為不該加動畫的地方加上動畫。 下方的門檻機制有時就是為了產出「零行程式碼」而存在的。這是成功的決策,而不是逃避。
  2. 在正確的地方用錯了動畫元素——例如在進場時使用 ease-in、使用 scale(0)、在 toast 提示上用 keyframes,或是時長太長讓下拉選單顯得卡頓遲緩。

絕不要把動態選項當成菜單拿給使用者挑選。直接做出最佳判斷,用一說明理由,然後撰寫程式碼。

硬性規則

  1. 按順序執行流程。 步驟 1 和 2 是所有決策的關卡。在確定是否需要動畫之前,不要先挑選速度曲線。
  2. 禁止使用憑感覺估算的數值。 所有曲線、時長與彈簧配置都必須來自下方表格。切勿因為看起來眼熟就隨手寫下 cubic-bezier(0.4, 0, 0.2, 1)
  3. 延伸既有程式碼庫的 Token,不要另起爐灶。 若專案中已存在 --ease-out 或時長階梯,請直接使用。建立另一套平行系統屬於程式缺陷。
  4. 減弱動態(Reduced motion)與懸停限制(hover gating)必須隨動畫一同交付,而不是事後補上。
  5. 使用能搞定需求的最低成本工具。 不要為了單純的淡入淡出就安裝套件。

建構流程

1. 到底需不需要動畫?

頻率 決策
每天 100 次以上(快速鍵、指令面板開關) 絕不使用任何動畫。 到此為止。
每天數十次(懸停效果、列表導覽) 僅限微不可察的效果——極快且微弱,否則就不加
偶爾(對話框 modal、抽屜 drawer、toast 提示) 標準動畫
罕見 / 初次(新手引導 onboarding、成功提示、慶祝動畫) 驚喜預算專屬於此

透過鍵盤觸發的動作直接排除動畫,這不是主觀判斷。 Raycast 完全沒有開啟/關閉動畫——對於每天要開啟數百次的工具來說,這才是正確做法。

若需求未通過此關卡,請直接坦白說明,並且不要撰寫動畫程式碼。改為提供無動態的替代方案(例如瞬間狀態切換、靜態視覺提示)。

2. 目的為何?

在繼續之前,請先從以下詞彙中挑選一個來明確定義其目的:

  • Feedback(反饋)——確認介面已接收到使用者的操作
  • Spatial consistency(空間一致性)——展現元素來自何處或去向何方
  • State indication(狀態指示)——讓狀態變更清晰易懂
  • Preventing a jarring change(避免突兀跳變)——為原本會瞬間硬切的內容提供銜接過渡
  • Explanation(解說說明)——示範功能運作方式(僅限行銷/新手引導)
  • Delight(驚喜感)——僅限用於罕見或首次發生的情境

無法明確歸類?那就不要打造。在頻繁出現的元素上喊出「這樣看起來很炫」,正是該打住的理由。

同時也要檢查功能性:使用者正在閱讀或操作的數據不應為了視覺造型而移動。裝飾性的游標追蹤效果屬於行銷頁面,而不是網路銀行 App 裡的圖表。

3. 選擇工具——能搞定需求的最低成本方案

由上往下評估,停在第一個滿足需求的選項。

需求 工具
懸停 (hover)、按壓 (press)、顏色、透過 class 或 attribute 控制的狀態切換 CSS transition
元件掛載 (mount) 時的進場動畫,無 JS 狀態 CSS @starting-style
預先設定好且在頁面繁忙載入時仍須保持順暢的動態 CSS animation(於主執行緒外運作)
具備 CSS 效能的程式化控制,無需額外套件 WAAPI (element.animate())
彈簧物理效果 (springs)、版面配置動畫 (layout animations)、退場動畫 (exit animations)、手勢驅動數值 Motion (motion.dev)

在高負載情況下,CSS 動畫勝過 JS——因為它們在主執行緒之外執行;而基於 requestAnimationFrame 的動畫會在瀏覽器進行載入、執行腳本或繪製時掉幀。預先確定的動態請使用 CSS,動態且可打斷的動態請使用 JS。

若任務需要的是一個組件而非單純的動畫——例如 toast 提示、抽屜 drawer、指令選單、下拉選單——請暫停並呼叫 pick-ui-library。手刻這些組件往往會導致做出一堆沒有焦點管理的 <div> 下拉選單。

4. 選擇屬性

  • 僅限使用 transformopacity 它們能避開版面配置(layout)與繪製(paint),直接由 GPU 渲染。而 width/height/margin/padding/top/left 則會觸發全部三者。(clip-path 是特許的第四個屬性——參見 RECIPES.mdheight 僅在手風琴 accordion 元件中被容許使用,因為該處沒有等效的 transform 替代方案。)
  • 絕不使用 scale(0) 請從 scale(0.9–0.97) + opacity: 0 開始。真實世界中沒有任何東西會從零瞬間憑空出現。
  • 浮動面板 (popover)、下拉選單 (dropdown)、功能表 (menu)、工具提示 (tooltip) 的 transform-origin 必須設定在觸發點——例如 Base UI 中的 var(--transform-origin)Modal 對話框例外;因為它們並未錨定於特定觸發點,所以保持置中。
  • translate() 中的百分比是相對於元素自身的尺寸——不論內容為何,translateY(100%) 都會移動其自身的高度。請優先使用百分比而非寫死的像素值。
  • 在 Motion 中,請使用完整的 transform 字串。 x/y/scale 等簡寫並未經過硬體加速,在繁忙負載下會掉幀:
<motion.div animate={{ x: 100 }} />                          // 負載下會掉幀
<motion.div animate={{ transform: "translateX(100px)" }} />  // 硬體加速
  • 切勿透過父元素的 CSS 變數來驅動子元素的 transform——這會導致每個子元素都重新計算樣式。請直接在元素本身設定 transform

5. 速度曲線與時長——或是彈簧效果

Easing(速度曲線),依決策順序:

情境 Easing
進場或退場 ease-out
螢幕上移動 / 形變 ease-in-out
懸停 / 顏色變更 ease
持續動態(跑馬燈、進度條) linear
預設 ease-out

UI 介面上絕不要使用 ease-in 它起步緩慢,會延遲使用者注視的瞬間。200ms 的 ease-out 體感上比 200ms 的 ease-in 感覺更迅速。

CSS 原生內建的速度曲線效果太弱。請改用以下設定:

--ease-out: cubic-bezier(0.23, 1, 0.32, 1);        /* 適用於 UI 的強效 ease-out */
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);    /* 適用於螢幕上移動的強效 ease-in-out */
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);     /* 類似 iOS 抽屜的曲線 (Ionic) */

需要這裡沒列出的曲線?請直接從 easing.deveasings.co 複製。不要自己憑空捏造。

Duration(時長):

元素 時長
按鈕按壓反饋 100–160ms
工具提示、小型 popover 125–200ms
下拉選單、選擇器 150–250ms
對話框 modal、抽屜 drawer 200–500ms
行銷 / 功能解說 可適度拉長

UI 動畫時長應保持在 300ms 以下。 180ms 的下拉選單體感上比 400ms 更加即時流暢。

當動態屬於帶有慣性的拖曳、希望元素具備生動有機感、使用者可隨時打斷或反轉的手勢操作,或是裝飾性的游標追蹤時,請改用彈簧效果 (spring)

{ type: "spring", duration: 0.5, bounce: 0.2 }        // Apple 風格——更容易理解與推導
{ type: "spring", mass: 1, stiffness: 100, damping: 10 }  // 傳統物理參數——掌控度更高

將 bounce(彈跳度)保持在 0.1–0.3 之間,且在多數 UI 中應避免彈跳效果——將其保留給滑動清除(drag-to-dismiss)或趣味性互動。

6. 打斷與退場

  • 頻繁觸發的元素請使用 transition 而非 keyframes——例如 toast 提示、開關 toggle,或任何使用者可能在一秒內點擊兩次的元素。Transition 會從當前數值重新設定目標,而 keyframes 則會歸零重新播放。
  • 手勢操作請使用 spring,因為它們在被打斷時能延續原本的初速度。
  • 退場方式應與進場對稱。 從底部滑入的 toast 就該往底部退場。對稱的路徑能讓滑動清除 (swipe-to-dismiss) 的直覺感油然而生。
  • 使用者決策階段採用非對稱時序。 在使用者主動操作的階段放慢速度(例如長按確認:2s linear),而在系統回應階段則保持俐落(例如放開按鈕:200ms ease-out)。

7. 減弱動態與指標裝置限制

每次交付動畫時都必須包含此設定。

@media (prefers-reduced-motion: reduce) {
  .element { animation: fade 0.2s ease; } /* 保留透明度/顏色變化,移除基於 transform 的位移動態 */
}

@media (hover: hover) and (pointer: fine) {
  .element:hover { transform: scale(1.05); } /* 觸控螢幕在點擊時會誤觸懸停效果 */
}
const reduce = useReducedMotion();
const closedX = reduce ? 0 : '-100%';

減弱動態(Reduced motion)意味著減少且更平緩的動畫,而非完全歸零——保留有助於理解介面的過渡效果,僅移除位移與位置變動。

常用範例

常見情境的開箱即用實作範例——包含按鈕按壓、下拉選單、工具提示、modal、抽屜、toast、手風琴、交錯動畫 (stagger)、長按確認、頁籤指示器 (tab indicator)、捲動揭露 (scroll reveal)、滑動清除 (drag-to-dismiss)——請參閱 RECIPES.md。只要需求符合上述組件,請載入該檔案並以此為起點,而不是從空白檔案開始。

絕不交付的禁忌

完成前請自我檢查。以下每一項在 review-animations 中都會被直接退件:

禁止行為 替代做法
transition: all 明確指定要過渡的屬性
transform: scale(0) 進場 scale(0.95) + opacity: 0
UI 元素上使用 ease-in 改用 ease-out 或強效自訂曲線
刻意打造的動畫中使用原生 ease-out cubic-bezier(0.23, 1, 0.32, 1)
快速鍵或每天觸發 100 次以上的動作加上動畫 完全不加動畫
無特殊理由時 UI 時長超過 300ms 150–250ms
錨定特定觸發點的 popover 使用 transform-origin: center var(--transform-origin)(modal 對話框例外)
Toast 提示、開關 toggle、頻繁觸發的元素使用 keyframes CSS transitions
width/height/margin/padding/top/left 施加動畫 transform / opacity
在負載下使用 Motion 的 x/y/scale 簡寫屬性 使用完整的 transform 字串
未設定限制條件的 :hover 動態 @media (hover: hover) and (pointer: fine)
缺少 prefers-reduced-motion 改用平緩版本而非歸零
所有元素同時進場 30–80ms 的交錯動態

輸出內容

撰寫程式碼。接著,用最多數行說明:

  • 門檻檢查結果——頻率階層與明確定義的目的。若需求中有任何部分被拒絕,請說明是何者及原因。
  • 動畫要素——工具、屬性、曲線、時長或彈簧設定,各佔一行。
  • 體感確認要點——若成果取決於無法單從程式碼判斷的體感(例如交叉淡入淡出 crossfade、彈簧的 bounce、進場列表中透明度與高度的平衡),請明確指出並提示檢查方式:以 2–5 倍時長播放或使用 DevTools 動畫檢視器、逐幀檢查、在真實裝置上測試手勢,並在隔天帶著全新的視角再次審視。

不要將其擴充成冗長報告。程式碼才是最終交付物。

語氣風格

觀點鮮明、簡潔俐落。當坦誠的答案是「這不該加動畫」時,請直接說出來——這個答案正是本 Skill 存在的意義。當體感確實無法由程式碼決定時,請坦白說明,而不是隨便猜測一個數值。