懶人包
- IG 設計帳號 @chloemakesdesign 的 Reel 教人用 Claude Code 加 Motion 做會動的飲料官網,核心技巧是把每個動效的「誰動、從哪裡來、怎麼動」寫清楚,不要只寫「加一點動畫」。
- 我用
claude -p對同一份品牌描述跑了 12 次:Sonnet 5.5「加一點動畫」4 次、四種動效寫清楚 4 次,另外 Haiku 5.5「加一點動畫」4 次。- 「加一點動畫」在我這輪實驗裡不只給淡入:Sonnet 4 次都寫了位移、旋轉、stagger 和 inView,每頁 15~22 個
animate()。- 差別在「誰決定」:模糊版由模型自己挑,頁面載入 6 秒後還有 7~39 個動畫在跑;寫清楚的 4 次,6 秒後 0 個,10 項規格參數 4 次全數對上。
- 可以直接抄的是一張四欄動效規格:元素、起點、方式、觸發時機,外加一行
prefers-reduced-motion的退路。
這篇的起點是 IG 帳號 @chloemakesdesign 在 2026-11-01 發的一支 Reel:一個叫「柚光」的虛構飲料品牌官網,杯子和柚子都是程式畫的,整站由 Claude 做出來。她的步驟是四個:裝 Claude Code、叫它用 Motion、講清楚品牌、點名四種動效(進場、視差、逐字、滑入加按壓)。
她附的小技巧是這句:「不要只寫『加一點動畫』,Claude 多半只會給你淡入。把每個動效『誰動、從哪裡來、怎麼動』寫清楚,網站馬上有質感。」
我想知道這兩種寫法實際差多少,所以用自己的虛構品牌「梅窗」跑了 12 次對照。結果跟原文的說法不完全一樣,但她的技巧在我的數據裡依然成立,只是理由換了一個。
Motion 是什麼?跟 Framer Motion 什麼關係?
Motion 是一套網頁動畫函式庫,官方文件形容它是「hybrid engine」:結合瀏覽器原生動畫的效能與 JavaScript 引擎的彈性,可以動 HTML、CSS、SVG。它原本叫 Framer Motion,官方的升級指南寫得很直接:npm uninstall framer-motion、npm install motion,然後把 import 從 "framer-motion" 換成 "motion/react"。
除了 React 版,它也有不用框架的 vanilla JS 版。這次實驗用的是 jsDelivr 上的 UMD 檔,一個 <script> 就能載入,API 掛在全域變數 Motion 底下:
| 項目 | 2026-11-01 查證結果 |
|---|---|
| npm 最新版 | motion 14.9.0(npm registry 標示 2026-10-30 發布) |
| CDN 載入 | https://cdn.jsdelivr.net/npm/motion@14.9.0/dist/motion.js(HTTP 200) |
| UMD 匯出(我在 Node 裡載入後檢查) | animate、scroll、inView、stagger、press、hover、spring 都是 function |
| 不在 UMD 裡的 | splitText(逐字要自己拆 span) |
animate 搭 spring,視差用 scroll,逐字用 stagger,滑入用 inView,按壓用 press 或自己綁 pointerdown。
「加一點動畫」和寫清楚的 prompt,差在哪?
兩份 prompt 的前半完全一樣:品牌是「梅窗」,賣青梅、烏梅系列手搖飲,深梅紫 #4a1f3d 配青梅綠 #9bc53d、米白底,感覺是清爽、有點復古的台式茶行,頁面要有主視覺杯子、標語、4 張菜單卡、門市資訊,指定用上面那個 Motion CDN。
模糊版在後面只接一句「加一點動畫。」
具體版接的是下面這段,也就是原作者說的「誰動、從哪裡來、怎麼動」,我把四種動效逐一寫成規格。實際送出的 prompt 是一種動效一行,這裡為了好讀拆成短行,內容沒改:
動效有四種,每一種的
「誰動、從哪裡來、怎麼動」如下:
1. 進場
- 頁面載入時,主視覺的杯子
從畫面上方 120px 處掉下來
- 用 spring(bounce 約 0.35)落定,
同時從 0.8 倍放大到 1 倍
- 杯子落地後,副標和按鈕
依序從下方 20px 浮上來
2. 視差
- 主視覺周圍放 5 個裝飾物
(梅子、葉子、冰塊)
- 捲動時用 Motion.scroll 讓它們
依各自速度位移,有的往上、有的往下,
速度從 -0.6 到 0.5 不等
- 杯子本身往上飄並微微縮小
3. 逐字
- 主標題拆成單字,每個字從下方 0.6em、
旋轉 -8 度、透明開始
- 用 Motion.stagger 間隔 0.04 秒
逐字翻上來
4. 滑入+按壓
- 菜單卡片用 Motion.inView,
卡片露出 30% 時才觸發
- 單數卡從左邊 80px 滑入、
偶數卡從右邊 80px 滑入
- 按下卡片(pointer down)立刻縮到 0.96,
放開時用 spring 彈回 1
另外:使用者開了 prefers-reduced-motion 時,
全部改成只有淡入。
我怎麼測的?
每次都是一個全新的 claude -p,在空資料夾執行、關掉所有工具與 MCP,只讓模型輸出 HTML:
cd empty
claude -p --model sonnet --tools "" \
--strict-mcp-config --output-format json \
< prompt-vague.txt > runs/vague-1.json
| 組別 | 模型 | 次數 | 單次耗時 |
|---|---|---|---|
| 模糊版「加一點動畫」 | Sonnet 5.5 | 4 | 79~95 秒 |
| 具體版(四種動效規格) | Sonnet 5.5 | 4 | 111~512 秒 |
| 模糊版「加一點動畫」 | Haiku 5.5 | 4 | 70~363 秒 |
- 讀程式碼:用 Python 掃
<script>裡有沒有animate()、位移/縮放/旋轉、stagger、inView、scroll、按壓或 hover,以及具體版要求的 10 個參數(-120、bounce 0.35、0.8→1、scroll()、stagger 0.04、rotate -8、inView 30%、±80px、0.96、prefers-reduced-motion)。 - 實際跑:Playwright 用 1280×800 開每個頁面,0.25 秒和 6 秒各截一張圖,記錄 console 錯誤,並在 6 秒時用
document.getAnimations()數還在 running 的動畫。這個數字只算得到瀏覽器原生動畫(WAAPI),Motion 用 JS 驅動的部分不在裡面,所以是下限。
「加一點動畫」真的只給淡入嗎?
在我這輪實驗裡沒有。
Sonnet 5.5 收到「加一點動畫」的 4 次,每一次都寫了 15~22 個 animate(),4 次都有位移、縮放、旋轉、stagger 和 inView,其中 3 次還用了 spring。挑幾段它自己寫的:
| 次 | 模型自己決定的動效(節錄) |
|---|---|
| 1 | 標題逐字上浮、杯子從上方 90px 帶旋轉落下、杯子持續漂浮、氣泡上升、卡片 hover 抬起 |
| 2 | 標題逐行升起、杯子 spring 落下、液面注入、波浪左右晃、冰塊浮沉、梅子 18 秒轉一圈、花瓣漂浮、跑馬燈 |
| 3 | 背景圓放大、虛線圈 40 秒轉一圈、杯子落下回彈、波浪、吸管晃動、氣泡、跑馬燈、卡片 hover 用 spring |
| 4 | 圓盤旋轉進場、標題逐行升起、印章旋轉蓋下、液體注入、吸管插入、冰塊和梅子晃動、跑馬燈、花瓣下落與氣泡上升 |
animate(),但 4 次也都有位移,不是純透明度淡入。
所以「Claude 多半只會給你淡入」這句,我用 2026-11 的 Sonnet 5.5 和 Haiku 5.5 在 claude -p 裡沒有重現。原作者沒有公開她用的模型和完整 prompt,環境不同結果可能不同;我的 prompt 前半已經指定 Motion、寫了品牌顏色與感覺,這些資訊本身就可能讓模型更願意加動畫。
那寫清楚還有必要嗎?差在「誰決定」
有,而且在數據裡很明顯。
| 指標 | 模糊版 Sonnet(4 次) | 具體版 Sonnet(4 次) | 模糊版 Haiku(4 次) |
|---|---|---|---|
| 6 秒時仍在跑的動畫 | 21、9、7、39 | 0、0、0、0 | 11、1、9、5 |
| 程式碼裡的無限循環 | 4~9 處 | 0 處 | 2~6 處 |
| 10 項規格參數命中 | 只有零星巧合 | 10/10(4 次全中) | 只有零星巧合 |
| 減少動態效果處理 | 4/4 | 4/4 | 2/4 |
| 頁面錯誤 | 0 | 0 | 1 次 |
repeat: Infinity 和 CSS infinite;減少動態效果指有沒有處理 prefers-reduced-motion;Haiku 那次錯誤是 Cannot read properties of undefined (reading 'closest')。
模糊版的問題不是動畫太少,是動畫由模型挑,而且停不下來。漂浮的杯子、冒泡、旋轉的印章、跑馬燈都是 repeat: Infinity,頁面打開 6 秒後,第 4 次還有 39 個瀏覽器動畫同時在跑。每一次挑的組合也不一樣:第 2 次有液面注入和花瓣,第 3 次有旋轉的虛線圈,第 4 次有蓋下來的印章。
具體版 4 次全部照規格:杯子 y: [-120, 0]、scale: [0.8, 1]、bounce: 0.35,標題 stagger(0.04) 加 rotate: [-8, 0],卡片 inView(..., { amount: 0.3 }) 單數從左、偶數從右 80px,按壓用 pointerdown 縮到 0.96。動畫播完就停,6 秒後 0 個在跑。
靜態截圖看不出動態,兩邊的版面品質也差不多。真正的差別要打開頁面看:模糊版是一直在動的熱鬧頁面,具體版是進場有戲、之後安靜的頁面。哪個比較好是設計判斷,但只有後者是你決定的。
具體版有一個代價:4 次耗時 111~512 秒,模糊版是 79~95 秒。claude -p 回報的 total_cost_usd 具體版也略高(0.32~0.45 美元對 0.28~0.30 美元)。
動效規格要寫哪幾欄?
把原作者的「誰動、從哪裡來、怎麼動」拆開,我在具體版 prompt 裡每一條都包含這四欄:
| 欄位 | 寫什麼 | 梅窗的例子 |
|---|---|---|
| 誰動 | 哪個元素,越具體越好 | 主視覺的杯子、主標題的每個字、單數/偶數菜單卡 |
| 從哪裡來 | 起始狀態:位置、角度、大小、透明度 | 上方 120px、0.8 倍;下方 0.6em、-8 度、透明 |
| 怎麼動 | 曲線與節奏:spring 的 bounce、duration、stagger 間隔 | spring bounce 0.35;每字間隔 0.04 秒 |
| 什麼時候動 | 觸發條件 | 頁面載入、捲動、露出 30%、pointer down |
prefers-reduced-motion 時只淡入」,讓有開減少動態效果的使用者不會被晃到。這行在 Sonnet 兩組都有自動處理,Haiku 模糊版 4 次只有 2 次有。
如果你不想要任何持續循環的動畫,也可以直接寫一句「所有動畫播完就停,不要 repeat: Infinity」。我這輪沒有單獨測這句,具體版是因為規格裡沒有要求循環,模型就沒加。
想要現成的動效名稱,可以搭配 Motion Prompt 的 168 個動畫名稱,或用 animation-vocabulary 把「彈一下」反查成標準術語,再把名稱填進「怎麼動」那一欄。
這次實驗的限制
- 樣本小:每組 4 次,能看出方向,不能當成統計結論。
- 全域設定沒有完全隔離:
--bare模式需要 API key,我用的是訂閱登入,所以使用者層的 CLAUDE.md 仍會載入。工具全關,模型無法讀取任何 skill。 - 規格命中是 regex 掃程式碼:模糊版偶爾會巧合命中(例如第 3 次剛好也用了 bounce 0.35、第 4 次的印章旋轉 -120 度撞到
-120),所以表格只寫「零星巧合」,不給精確分數。具體版的 10 項我另外讀過第 1 次的完整程式碼確認語意正確。 - 6 秒 running 數是下限:只算 WAAPI 動畫。
- 沒有比較視覺品質:版面、配色、插畫哪組比較好看,我沒有評分。
想自己重現要準備什麼?
- Claude Code(我用 2.1.316),登入後能跑
claude -p。 - 一份品牌描述:賣什麼、什麼顏色、什麼感覺、頁面要有哪些區塊。
- 指定 Motion 的載入方式。單檔 HTML 用 CDN 最快;React 專案就
npm install motion,從motion/reactimport。 - 照上面四欄寫動效規格,一種動效一段。
- 打開頁面停 6 秒看看還有什麼在動。想量化的話,在 DevTools console 跑
document.getAnimations().filter(a => a.playState === 'running').length。
常見問題
用 Claude Code 做動畫網站一定要用 Motion 嗎?
不用。純 CSS transition、Web Animations API、GSAP 都可以。這篇用 Motion 是照原 Reel 的做法,它的 scroll、inView、stagger、spring 剛好對應四種動效,寫規格時有現成的詞可以用。
只寫「加一點動畫」會怎樣?
在我的 12 次實驗裡,Sonnet 5.5 和 Haiku 5.5 都不只給淡入,會自己加位移、逐字、漂浮、跑馬燈。差別是動效由模型挑、每次不同,而且常帶無限循環:Sonnet 模糊版 6 秒後還有 7~39 個動畫在跑。
規格要寫到多細?
至少寫元素、起始狀態、曲線節奏、觸發時機四欄。數字可以先抓大概(120px、0.04 秒),看完效果再調;我的具體版 4 次都完整照數字實作。