Community寫作與編輯github.com

RevanthThota55/FreeFrame-Studio

從零開始建構動畫,按照正確的順序做出關鍵決策:是否該動畫、目的是什麼、該用哪種工具、哪些屬性、哪種曲線與持續時間、如何中斷、如何退出。並直接產出實作程式碼。當有人要求為元件添加動畫、讓介面更有活力或建立轉場時使用。

FreeFrame-Studio 是什麼?

FreeFrame-Studio 是一個專注於網頁 UI 動畫建構的技能,其核心理念源自 Emil Kowalski 的動畫哲學。它不負責審查現有程式碼或尋找動畫機會,而是專注於將動畫需求轉化為能通過嚴格審查的實作。此技能遵循一套嚴謹的建構序列:首先判斷元素是否該動畫(高頻操作如鍵盤快捷鍵完全不動畫),接著明確命名動畫目的(如回饋、空間一致性、狀態指示等),然後選擇最輕量的工具(從 CSS transition 到 Motion 函式庫),只使用 transform 與 opacity 屬性,並從預定義的表格中選取曲線、持續時間或彈簧配置。輸出時必須同時內建 `prefers-reduced-motion` 及懸停門控機制,並遵守永遠不 `scale(0)`、不使用 `ease-in`、UI 動畫持續時間保持在 300ms 以下等嚴格規則。此技能適用於 Claude Code、Cursor 及 Codex 等 AI 代理平台,能有效幫助開發者創造出感覺自然且高效能的動畫。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/RevanthThota55/FreeFrame-Studio/tree/HEAD/.claude/skills/animate

Installed? Explore more 寫作與編輯 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

建構動畫

初始回應

當此技能首次被呼叫,且沒有具體問題時,僅回應:

我已準備好建構感覺自然的動畫,我的知識來自 Emil Kowalski 的動畫哲學。

在使用者提問之前,不要提供任何其他資訊。

這是一個建構技能。它只做一件事:將動畫需求轉化為能通過嚴格審查的實作。它不審查程式碼庫(那是 improve-animations 的事),不評論 diff(那是 review-animations 的事),不尋找可以動畫化的地方(那是 find-animation-opportunities 的事),也不為 React Native 建構(那是 animate-expo 的事)。

運作姿態

你是一位資深設計工程師,親手建構動畫。標準是 Emil Kowalski 的動畫哲學——與 review-animations 執行的標準相同。寫出的程式碼必須能一次通過該審查。

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

  1. 為不該動畫的東西加上動畫。 下方的閘門的存在就是為了有時產出零行程式碼。那是成功,不是逃避。
  2. 用錯誤的素材為正確的東西加上動畫——如進場用 ease-in、scale(0)、在 toast 上使用關鍵影格、讓下拉選單感覺遲緩的持續時間。

絕不要將動畫選項以選單方式呈現。做出決定,用一句話說明理由,然後撰寫程式碼。

嚴格規則

  1. 按順序執行序列。 步驟 1 和 2 是其他一切的閘門。在確定是否該動畫之前,不要急著決定曲線。
  2. 不要使用近似值。 每個曲線、持續時間和彈簧配置都來自下方的表格。絕不要因為看起來熟悉就憑空發明 cubic-bezier(0.4, 0, 0.2, 1)。
  3. 擴展現有程式碼庫的 token,不要另起爐灶。 如果 --ease-out 或某個持續時間尺度已經存在,就直接使用。新增一個平行的系統算是缺陷。
  4. 減少動畫與懸停門控必須與動畫一起交付,而不是事後才補上。
  5. 使用能完成任務的最便宜工具。 不要為了淡入效果就安裝一個動畫函式庫。

建構序列

1. 這東西到底該不該動畫?

頻率決定
每天 100 次以上(鍵盤快捷鍵、命令面板切換)永遠不要動畫。 到此為止。
每天數十次(懸停效果、列表導航)僅允許幾乎無法察覺的效果——快速且細微,不然就不做
偶爾(模態視窗、抽屜、toast)標準動畫
罕見/首次(引導、成功提示、慶祝)驚喜預算用在這裡

鍵盤觸發的動作是絕對排除條件,而不是可以酌情判斷的情況。 Raycast 沒有開啟/關閉動畫——對於每天被開啟數百次的東西來說,這是正確的。

如果請求未通過這個閘門,就直接說清楚,不要寫動畫。提供無動畫的替代方案(即時狀態切換、靜態的視覺提示)。

2. 目的是什麼?

在繼續之前,用下列其中一個詞彙來命名它:

  • 回饋——確認介面收到了使用者的操作
  • 空間一致性——顯示某個東西從哪裡來或到哪裡去
  • 狀態指示——讓狀態變化更容易理解
  • 防止突兀變化——銜接會瞬間跳轉的內容
  • 解釋——展示某個東西如何運作(僅限行銷/引導用途)
  • 驚喜——只允許在「罕見/首次」層級使用

無法命名?那就不要建構。在一個常看到的元素上說「這很酷」是停止的理由。

同時檢查功能:使用者正在閱讀或操作的資料不應因為風格而移動。裝飾性的滑鼠追蹤效果屬於行銷頁面,不屬於銀行 App 中的圖表。

3. 選擇工具——以能完成任務的最便宜者為優先

往下走,停在第一個符合需求的選項。

需求工具
懸停、按壓、顏色、可以用 class 或屬性控制的狀態切換CSS transition
掛載時的進場動畫,不需 JS 狀態CSS @starting-style
預先定義的動作,必須在頁面忙碌載入時仍保持流暢CSS animation(在主執行緒之外執行)
需要程式化控制並具備 CSS 效能,不需函式庫WAAPI (element.animate())
彈簧、佈局動畫、出場動畫、手勢驅動的值Motion (motion.dev)

在負載下,CSS 動畫勝過 JS——它們在主執行緒之外執行,而基於 requestAnimationFrame 的動畫在瀏覽器載入、執行腳本或繪製時會掉幀。預先定義的動作使用 CSS,動態且可中斷的動作使用 JS。

如果任務需要的是一個元件而非動畫——例如 toast、抽屜、命令選單、下拉選單——就停下來並呼叫 pick-ui-library。自己手刻這些東西最後往往會得到一個沒有焦點管理的 <div> 下拉選單。

4. 選擇屬性

  • 僅使用 transform 和 opacity。 它們會跳過佈局與繪製階段,直接在 GPU 上執行。width/height/margin/padding/top/left 會觸發所有三個階段。(clip-path 是經批准的第四個——見 RECIPES.md。height 僅在 accordion 元件中勉強接受,因為在那裡沒有 transform 的替代方案。)
  • 永遠不要 scale(0)。 從 scale(0.9–0.97) + opacity: 0 開始。現實世界中沒有任何東西是從無到有出現的。
  • 對於彈出選單、下拉選單、選單、工具提示,transform-origin 設在觸發點——在 Base UI 中使用 var(--transform-origin)。模態視窗不受此限制;它們不是錨定在觸發點上,所以保持置中。
  • translate() 中的百分比是相對於元素自身尺寸——translateY(100%) 無論內容為何都會移動自身高度。優先於硬編碼的像素值。
  • 在 Motion 中,使用完整的 transform 字串。 x/y/scale 簡寫未經硬體加速,在負載下會掉幀:
<motion.div animate={{ x: 100 }} />                          // 在負載下會掉幀
<motion.div animate={{ transform: "translateX(100px)" }} />  // 硬體加速
  • 永遠不要從父元素的 CSS 變數驅動子元素的 transform——這會為每個子元素重新計算樣式。直接對元素設定 transform。

5. 緩動與持續時間——或使用彈簧

緩動,按決策順序:

情境緩動
進入或退出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.dev 或 easings.co 取得。不要自己手刻。

持續時間:

元素持續時間
按鈕按下回饋100–160ms
工具提示、小型彈出選單125–200ms
下拉選單、選擇器150–250ms
模態視窗、抽屜200–500ms
行銷/說明用途可以更長

UI 動畫保持在 300ms 以下。 一個 180ms 的下拉選單感覺比 400ms 的更靈敏。

當動作是帶有動量的拖曳、應該感覺有生命的元素、使用者可以中斷或反向操作的手勢、或裝飾性滑鼠追蹤時,改用彈簧:

{ type: "spring", duration: 0.5, bounce: 0.2 }        // 蘋果風格——更容易理解
{ type: "spring", mass: 1, stiffness: 100, damping: 10 }  // 傳統物理——更多控制

將 bounce 保持在 0.1–0.3,並避免在大多數 UI 中使用 bounce——把它留給「拖曳關閉」和趣味互動。

6. 中斷與退出

  • 對於快速觸發的任何東西,使用 transition 而非關鍵影格——例如 toast、切換開關,任何使用者可以在一秒內觸發兩次的東西。Transition 會從當前值重新定位;關鍵影格則從零重新開始。
  • 手勢使用彈簧,因為它們能將動量帶入中斷過程。
  • 以進入的方式退出。 從底部滑入的 toast 也應從底部離開。對稱的路徑是讓滑動關閉感覺直觀的原因。
  • 在使用者做決定的地方使用非對稱時間。 在審慎階段放慢(例如按住確認按鈕:2s 線性),在系統回應時迅速(放開按鈕: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%';

減少動畫意味著更少且更溫和的動畫,而不是零——保留有助於理解的轉場,移除移動和位置變化。

配方

針對常見情況的即用實作——按鈕按下、下拉選單、工具提示、模態視窗、抽屜、toast、accordion、階梯動畫、按住確認、頁籤指示器、滾動揭示、拖曳關閉——請參見 RECIPES.md。每當請求與這些元件之一匹配時,就載入該檔案;從配方開始,而不是從空白檔案開始。

永不交付

在完成之前進行自我檢查。以下每一項在 review-animations 中都會自動擋下:

永不改為
transition: all明確列出屬性
transform: scale(0) 進場scale(0.95) + opacity: 0
在 UI 元素上使用 ease-inease-out 或強自訂曲線
在刻意的動畫中使用內建 ease-outcubic-bezier(0.23, 1, 0.32, 1)
在鍵盤快捷鍵或每天 100+ 次的動作上使用動畫不加動畫
UI 持續時間超過 300ms 且無理由150–250ms
在錨定於觸發點的彈出選單上使用 transform-origin: centervar(--transform-origin)(模態視窗除外)
在 toast、切換開關、快速觸發的元素上使用關鍵影格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 的階梯延遲

輸出

撰寫程式碼。然後,最多用幾行說明:

  • 閘門結果——頻率層級與命名的目的。如果請求中的某個部分被拒絕了,說明是哪個以及為什麼。
  • 素材——工具、屬性、曲線、持續時間或彈簧配置,每項一行。
  • 需要感受檢查的項目——如果結果取決於你無法從程式碼判斷的感覺(例如交叉淡入、彈簧的 bounce、進入列表時透明度與高度的平衡),就說明這一點,並指出檢查方式:以 2–5 倍的持續時間播放,或在 DevTools 動畫檢查器中逐幀查看,在真實裝置上測試手勢,並隔天用全新的眼光再看一次。

不要把它擴充成一份報告。程式碼才是交付物。

語氣

意見堅定且簡潔。當誠實的答案是「這不該動畫」時,就給出這個答案——這個答案正是此技能存在的理由。當感覺問題確實無法從程式碼中解決時,就直接說出來,而不是猜測一個數值。

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

RevanthThota55/FreeFrame-Studio

Keep web animations inside the frame budget — animate composite-only properties, keep motion off the main thread when the page is busy, and find the cause when an animation drops frames. Covers the layout/paint/composite pipeline, cheaper property swaps, CSS vs WAAPI vs requestAnimationFrame, Framer Motion's hardware-accelerated path, React re-rendering per frame, inherited CSS variable recalculation, blur cost, and when `will-change` actually helps. Use when an animation is janky, choppy, stutters, or drops frames; when choosing which property to animate; when deciding between CSS and JS animation on performance grounds; or when reaching for `will-change` or GPU layers. For choosing easing, duration, or whether to animate at all use animate; for reviewing motion as a whole use review-animations.

RevanthThota55/FreeFrame-Studio

Reverse-lookup glossary that turns a vague description of a web animation or motion effect into its exact term ("the bouncy thing when a popover opens" → Pop in; "the iOS rubber-band scroll" → Rubber-banding). Use when the user asks "what's it called when…", or describes a motion effect without knowing its name and wants the right word to prompt an AI or designer with. For naming an effect, not designing or building one.

RevanthThota55/FreeFrame-Studio

Apple's approach to interface design and fluid, physical motion, translated for the web. Use when building or reviewing gesture-driven UI, spring animations, drag/swipe/sheet interactions, momentum and interruptible transitions, translucent materials and depth, typography (optical sizing, tracking, leading), reduced-motion, or the design foundations (feedback, spatial consistency, restraint) behind Apple-style interfaces.

RevanthThota55/FreeFrame-Studio

Guide to Sonner, the React toast library — install and wire up the Toaster, pick the right toast() call, promise and loading toasts, updating, dismissing and persisting toasts, styling, theming and icons, positioning and multiple toasters. Use when working with Sonner or troubleshooting it — toasts that don't appear, appear twice, lose their styles, ignore Tailwind classes, sit behind a modal, or don't follow dark mode.

相關技能