Communitygithub.com

pafelab/stretchy-morph-ui

將網頁 UI 轉換為伸縮變形 UI,依靠彈簧物理而非緩動曲線來實現擠壓、拉伸、過衝與沉降的過渡效果。

stretchy-morph-ui 是什麼?

這個技能讓開發者能將靜態 UI 元件(如分頁、開關、按鈕、模態框、下拉選單、摺疊面板、輪播、訊息提示、側邊欄、日期選擇器、加入購物車等)轉變為具備材質感的彈性動畫。它包含完整的彈簧引擎、24 個可運作參考元件、程式碼掃描器、無頭瀏覽器檢查器,以及設計全新伸縮元件的指引。工作流程先掃描使用者現有程式碼,識別可伸縮的元件候選項,並在修改前請求確認。動畫採用阻尼諧波振盪器,透過半隱式歐拉法進行 8 子步積分,支援方向相關的剛度切換、體積保持、速度拉伸等技術,以創造自然物理感的微互動。它同時考慮無障礙設計,支援 `prefers-reduced-motion`,並為螢幕閱讀器正確使用 ARIA 屬性。

相容平台✓Claude Code~Codex CLI~Cursor✓Antigravity
npx skills add https://github.com/pafelab/stretchy-morph-ui/tree/HEAD

在你喜歡的 AI 中提問

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

說明文件

伸縮變形 UI

這個技能產生的介面中,每個狀態變化都會 變形:分頁指示器像蠕蟲一樣伸向下一頁、按鈕擠壓成載入圓圈再膨脹回來、產品圖片從卡片浮起、縮小成晶片後落入購物車。它源自於建構 24 元件的展示(在 assets/showcase.html 中),以及過程中所發現的錯誤與設計修正。

外觀來自一個核心概念:拉伸必有原因。 元素變形是因為其各部分不一致(一個邊緣比另一個先到達)、因為快速移動(沿路徑伸長)、因為被擠壓(為保持體積而向另一側鼓起),或因為剛被撞擊(產生晃動)。迴圈式的隨機晃動顯得廉價;有原因的拉伸則展現材質感。

第 0 步,每次皆須執行:審核使用者程式碼並確認

在開始建構或編輯任何內容前,找出使用者介面中哪些部分可以變成伸縮的,並讓他們選擇。詳細資訊、判斷標準與訊息模板請參閱 references/audit.md;簡述如下:

  1. 找到程式碼:貼上的程式碼、附件(在 claude.ai 上,列出 /mnt/user-data/uploads)、工作目錄或倉儲、Artifact 連結(使用 artifact 工具讀取),或使用者指明的來源。若無任何內容,說明您搜尋了什麼,並提供兩個選擇:分享程式碼,或從頭建構新內容。
  2. 掃描它:python scripts/audit_ui.py <路徑> 會報告堆疊、現有動畫、缺少的減少動畫處理,以及每個 UI 類別的候選項目(含 檔案:行號),並對應到展示元件與技術。
  3. 在上下文中閱讀每個匹配項目,僅保留真正候選。針對每個項目,記錄其位置、目前行為、將成為的變形描述句、工作量(小、中、大)及任何風險(元件庫標記、現有動畫庫、會遺失行動版選擇器的原生控制項、非常長的列表)。
  4. 優先排序:根據可見度 × 頻率 × 契合度,將重複項目分組(所有使用同一個 Button 的按鈕視為一個項目),上限約十個,並列出您建議不要採用的項目及其理由。
  5. 以使用者的語言請求確認:編號的建議列表,然後一個邀請他們選擇編號、接受所有建議,或輸入不在列表中但希望變為伸縮的其他部分的提問。若環境提供可點擊選擇工具則使用之,否則使用純文字。在此回合結束;在收到回覆前不要進行任何編輯。
  6. 建構他們選擇的內容加上他們新增的部分,然後依循下方的工作流程,並逐項回報。

如果使用者已指明想要伸縮的部分,仍需進行審核:將他們的部分列在最前面,將其餘部分作為選項提供,並保持提問簡潔。

檔案

所有路徑皆相對於此技能資料夾(包含此 SKILL.md 的資料夾),而非使用者的專案。在讀取或執行時,請相對於技能資料夾解析它們。

路徑內容開啟時機
assets/stretchy-core.js彈簧引擎與輔助工具(邊緣配對、分段指示器、體積縮放、SVG 邊緣鼓起、FLIP 重排序、拋射物件)貼入您建構的每個頁面。讀取一次以了解輔助工具。
assets/starter.html頁面骨架:淺色/深色 token、支援泰文的可變字體、減少動畫處理、內嵌核心、一個範例元件從頭開始新頁面
assets/showcase.html原始建構中的所有 24 個工作元件。每個 JS 區段以 /* ---------- tabs ---------- */ 格式的橫幅開始當請求符合目錄元件時,使用 grep -n "/\* ---------- " assets/showcase.html 並僅讀取該區段及其 CSS
references/audit.md如何尋找、掃描、判斷與優先排序使用者程式碼,以及確認訊息模板(泰文與英文)在第 0 步,每次皆須執行
references/techniques.md11 種伸縮技術的詳細說明,含程式碼與調整數值在您建構未建構過的內容前
references/components.md目錄:每個元件的配方、彈簧數值與注意事項當請求指名或類似目錄元件時
references/new-components.md如何為任何元件設計伸縮版本,以及 14 個不在展示中的完整設計當請求超出目錄範圍時
scripts/audit_ui.py使用者程式碼的掃描器:堆疊、現有動畫,以及伸縮候選項目(含 檔案:行號)在第 0 步,每次皆須執行
scripts/check_page.py無頭瀏覽器檢查:JS 錯誤、手機與桌機的橫向溢出、隱藏元素攔截點擊,以及動畫幀的連絡表每次建構完成後,在交付前

如果存在視覺設計技能(例如 frontend-design 或 ui-ux-pro-max),也請一併讀取。該技能決定頁面外觀(調色板、字體、佈局);此技能決定其動畫方式。動畫潤飾無法挽救一個外觀普通的頁面。

工作流程(使用者確認後)

  1. 為每個元件撰寫變形描述句。「當 觸發條件 時,形狀 A 變成 形狀 B,且因 原因 而拉伸。」例如:「當分頁被點擊時,指示器移動到該分頁,並因其前緣比後緣更硬而拉伸。」若您無法填入原因,請從下方表格中選取。這句話即為設計;其餘皆為實作。
  2. 選取技術 從表格中選取。大多數元件使用一個主要拉伸加上一個小的回饋晃動。
  3. 將靜止狀態定義為數值:寬度、高度、半徑、位置、進度 0 到 1。彈簧驅動數字,您透過 onUpdate 將它們寫入 style。CSS 過渡僅處理顏色與不透明度(以及短內容的淡入淡出)。
  4. 規劃內容。 文字與圖示在容器變形時不得重新流動或壓縮。為每個內容狀態在其最終大小提供獨立圖層,錨定(通常置中)並進行淡入淡出:快速退出(約 150 毫秒),延遲約 100–150 毫秒後進入,並帶有輕微縮放或模糊。容器的 overflow:hidden 在內容成長時揭示內容。
  5. 建構 從 assets/starter.html 開始,並內嵌核心。直接在彈簧回呼中撰寫樣式;切勿觸發每次影格的框架重新渲染。
  6. 無障礙與中斷處理(檢查清單如下)。
  7. 測試與觀察。 執行 scripts/check_page.py,然後擷取重要互動的動畫幀連絡表並實際觀察。原始建構中的加入購物車動畫「運作」但看起來廉價,直到檢查影格後才修復;修復方法請見「讓它變好看」。

拉伸的來源

情境技術(詳細內容請見 references/techniques.md)範例
某物在位置間移動邊緣配對:前緣與後緣各用一個彈簧,前緣較硬,根據方向交換分頁、輪播點、開關、選擇晶片、單選點、步進器、下拉選單高亮、評分膠囊、側邊欄導航
容器改變大小或形狀軸向分離:為寬度、高度、半徑使用獨立彈簧,硬度不同,因此一個軸領先動態島、FAB 轉表單、從按鈕彈出的模態框、下拉選單、加入購物車按鈕
某物快速變窄或變寬體積保持:根據速度縮放另一軸,scaleY = clamp(1 − v/k)傳送按鈕、搜尋欄位、下拉選單寬度
某物快速移動速度拉伸:沿移動方向伸長,垂直方向變細滑桿拇指、拋射的購物車項目、輪播卡片(傾斜)、工具提示
大型表面移動邊緣鼓起:將表面繪製為 SVG 路徑,其邊緣控制點偏移 −velocity側邊欄、底部面板
部件懸掛在移動部件上滯後跟隨者:較軟的彈簧追逐主彈簧;利用間隙實現傾斜或偏移滑桿數值氣泡、摺疊面板內部文字
點擊或到達的回饋踢擊:對處於 0 的靜止彈簧增加速度,它會晃動並安定下來核取方塊、晶片、購物車、星星、徽章、數量限制抖動
物件融合或分離黏稠濾鏡:模糊加 alpha 閾值,上方疊加銳利來源黏稠選單
一個元素變成另一個元素容器轉換:從觸發器的矩形開始,彈簧移動到目標矩形,內容淡入淡出模態框、FAB、購物車拋射
變化之間需要暫停透過中間形狀的序列化變形,該形狀具有意義(旋轉器、勾選標記)傳送按鈕、加入購物車按鈕
拖曳超過限制,或列表重排序橡皮筋(將過度滾動除以約 3)和 帶彈簧的 FLIP,保留速度輪播、面板、排序後的產品列表

引擎概述

Spring 是一個阻尼諧波振盪器,使用半隱式歐拉法以每影格 8 子步進行積分(影格 delta 上限為 1/30 秒,以防止背景分頁爆炸)。to(target) 會重新設定向而不重置速度,這使得快速點擊感覺連續而非重新開始。set(x) 跳躍動畫,drag(x, v) 跟隨指針並記錄速度,kick(v) 注入脈衝。阻尼在每次 tick 時乘以全域 bounce 因子,因此一個頁面級控制(軟 1.8、有彈性 1、伸縮 0.5)即可即時重新調整每個元件。在 prefers-reduced-motion: reduce 下,to() 會立即跳至目標並觸發 onRest,因此等待動畫的邏輯仍會執行。

阻尼比 ζ = c / (2√k)。UI 狀態變化在 ζ 0.6–0.8 時感覺合適;趣味回饋在 0.3–0.5;大於 1 則無過衝。展示中使用的預設值:

角色kc備註
邊緣配對的前緣420–52030–34先到達
後緣140–17017–19滯後,產生拉伸
容器寬度(變形)240–32018–24
容器高度(變形)150–17015–17比寬度慢,因此形狀先橫向拉伸
指針跟隨者(滑桿)65042緊密,但仍保有速度感
滯後跟隨者(氣泡)15011故意鬆散
踢擊回饋380–42012–16踢擊強度 6–10
可見過衝的高度(摺疊面板)21014

經驗法則

  • 限制所有變形。 控制:體積保持在 ±7–15% 內。小型快速物體(拋射晶片、滑桿拇指):最多拉伸 35–70%。始終將大小限制為正最小值,因為低阻尼彈簧可能下衝低於零。
  • 依方向交換剛度。 在邊緣配對中,面向移動方向的邊緣使用較硬彈簧。若不交換,指示器會正確地向一個方向拉伸,但反向時壓縮。
  • 回程時採用相反時序。 如果打開時寬度領先高度,關閉時讓高度領先寬度。形狀會沿不同路徑返回,讀起來像是彈性而非回放。
  • 狀態變化用彈簧,形狀重要的路徑用基於時間的曲線。 拋射物體需要可預測的弧線(二次貝茲曲線、ease-in-out 進度,約 800 毫秒)。獨立的 x/y 彈簧會產生飄忽路徑。兩種情況下都從影格間速度推導拉伸。
  • 保持內容直立。 要沿任意方向拉伸某物而不旋轉其圖片或文字,使用 rotate(a) scale(1+s, 1−s·0.45) rotate(−a)。
  • 每個元件一個大膽動畫。 元件的其餘部分保持靜態:顏色變化、短暫淡出。
  • 閒置時平靜。 無物自行晃動。最多在頁面載入時有一個編排好的時刻(展示在 900 毫秒後開啟動態島一次)。

讓它變好看(建構經驗)

第一個加入購物車版本將一個純色球體沿兩個獨立彈簧飛行,將按鈕縮成一個空橢圓並壓扁購物車。使用者說它不美觀。修復方法具有通用性:

  • 移動真實物體。 複製產品圖片,稍提高並加上陰影,然後將其縮小為一個仍顯示圖片的圓形晶片,同時飛行。抽象圓點讀起來像除錯輸出。
  • 中間形狀必須具有意義。 縮小的按鈕是空團塊,除非它顯示內容:旋轉器、自繪勾選標記、計數。加入按鈕現在變成一個帶勾選的綠色圓圈,然後成長為 − n + 步進器。
  • 控制項上的微妙體積。 按鈕上 ±18% 的垂直鼓起看起來像藥丸形雞蛋。±7% 讀起來像柔軟材質。
  • 到達需要接收者。 購物車圖示傾斜並下沉,一個環向外脈動,徽章彈出。必須有東西明顯接住被拋射的物體。
  • 不要剪裁狀態標記。 變形 overflow:hidden 膠囊內的徽章會被切掉;將其放在裁剪範圍外的包裝器中。
  • 不要重複資訊。 購物車膠囊顯示「3 pieces 195 baht」旁邊還有一個顯示 3 的徽章。現在僅顯示總額。
  • 以固定比例繪製插圖。 磁磚內百分比定位的 CSS 形狀會在磁磚變寬時拉伸。使用包含 viewBox 並居中於磁磚的內聯 SVG。

無障礙與穩健性檢查清單

  • 尊重減少動畫(核心已處理;確保 CSS 關鍵影格和過渡也在 prefers-reduced-motion 媒體查詢中被移除)。
  • 使用真正的 button、input、role="switch"、role="tab"、role="slider" 等,並搭配正確的 aria-* 狀態,在啟動動畫的同一個呼叫中更新。
  • 被變形隱藏的內容應設定為 inert(且不得攔截點擊:為不可見圖層設定 pointer-events:none,然後在可見狀態時重新啟用)。
  • 當焦點控制項消失於變形中時,有意識地移動焦點:移動到替代控制項,對話框關閉後移回觸發器,且僅在元素再次可見後(visibility:hidden 元素不可聚焦)。

…

[為翻譯截斷]

相關技能