Communitygithub.com

pafelab/stretchy-morph-ui

将网页 UI 转换为弹性变形 UI,过渡效果如同弹簧拉伸、挤压、过冲和稳定,而非缓动曲线。使用时,先审计用户现有代码中的可弹性化组件(标签页、开关、按钮、模态框、下拉菜单、手风琴、轮播、提示框、侧边栏、日期选择器、加入购物车等),列出可更改项,并在编辑前请求用户确认或添加自己的请求。

stretchy-morph-ui 是什么?

此技能将静态网页 UI 转变为弹性变形界面,其中每次状态变化都会产生“形态变化”:标签指示器像蠕虫一样拉伸到下一个标签,按钮压缩成加载圆圈再膨胀回去,产品图片从卡片上抬起、缩小成芯片并落入购物车。它源自构建一个包含 24 个组件的展示页面,并总结了其中的错误和设计修正。 核心思想是:**拉伸必有原因**——元素变形是因为其各部件不一致(一条边先于另一条到达)、移动过快(沿路径伸长)、被挤压(另一个方向膨胀以保持体积)或刚被击中(摆动)。循环的随机摆动显得廉价,而有原因的拉伸则像真实材料。 每次请求时,技能首先审计用户的代码库,扫描 UI 组件,识别适合弹性化的候选者,并按可见性、频率和匹配度排序。然后列出建议,请求用户确认或修改。确认后,根据内置的弹簧引擎和丰富的参考组件库,为每个状态变化选择恰当的技术(如边缘对、轴向分离、体积保持等)。 最终输出一个既符合功能需求又具有生动“生命感”的 UI,支持 Claude Code、Cline、Cursor 和 Codex 等主要 AI 代理平台。该代码库包含一个弹簧引擎、24 个工作参考组件、一个扫描器、一个浏览器检查器,以及一种设计新弹性组件的方法。

兼容平台✓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)、工作目录或仓库、工件链接(使用工件工具读取),或用户指向的源代码。如果没有代码,说明你找到了什么,并提供两种路径:分享代码,或从头构建新内容。
  2. 扫描它:python scripts/audit_ui.py <路径> 报告技术栈、现有动画、缺失的减少动效处理,以及每个 UI 类别的候选者及其文件:行号,每个都映射到展示组件和技术。
  3. 在上下文中阅读每个命中点,只保留真正的候选者。对于每个候选者,记录其位置、当前状态、它将变成的变形描述、工作量(小、中、大)以及任何风险(组件库标记、现有动画库、移动选择器会丢失的原生控件、非常长的列表)。
  4. 按可见性 × 频率 × 匹配度排序,合并重复项(所有使用一个共享的 Button 的按钮算作一个项目),将列表限制在约十个项目,并列出你建议不要添加的项及其原因。
  5. 用用户的语言请求确认:编号的提议,然后一个问题邀请他们选择编号、接受所有推荐,或输入不在列表中的他们想要弹性化的额外部分。如果环境支持,使用可点击选择工具,否则使用纯文本。在此结束该轮;在收到回复前不做任何编辑。
  6. 构建他们选择的以及他们添加的任何内容,然后按照下面的工作流程进行,并逐项报告。

如果用户已经命名了他们想要的部分,仍要进行审计:将他们的部分放在首位,将其他部分作为可选提供,并保持问题简短。

文件

所有路径都是相对于本技能文件夹(包含此 SKILL.md 的文件夹),而不是用户的项目。在读取或运行它们时,要相对于技能文件夹解析它们。

路径说明何时打开
assets/stretchy-core.js弹簧引擎及其辅助函数(边缘对、分段指示器、体积缩放、凸起的 SVG 边缘、FLIP 重新排序、抛射物体)粘贴到您构建的每个页面。阅读一次以了解辅助函数。
assets/starter.html页面骨架:亮/暗主题标记、支持泰语的可变字体、减少动效处理、内联核心、一个示例组件从此文件开始新页面
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 路径,其边缘控制点偏移 −速度侧边栏、底部导航面板
某个部分悬挂在一个移动部分上滞后跟随者:一个较软的弹簧跟随主弹簧;利用间隙实现倾斜或偏移滑块值气泡、手风琴内部文本
点击或到达的反馈冲击: 向静止在 0 的弹簧添加速度,它摆动并稳定复选框、芯片、购物车、星级评分、徽章、数量限制震动
模糊的块连接或分离粘性滤镜: 模糊加透明度阈值,清晰的源合成在上面粘性菜单
一个元素变成另一个元素容器变换: 从触发器的矩形开始,弹簧过渡到目标矩形,内容交叉淡入淡出模态框、FAB、购物车投掷
变化需要在中间暂停通过中间形状的顺序变形,该形状承载含义(旋转器、勾选)发送按钮、加入购物车按钮
拖拽超过限制,或列表重新排序橡皮筋(将过卷除以约 3)和带弹簧的 FLIP,保持速度轮播、面板、排序后的产品列表

引擎概述

Spring 是一个阻尼谐波振荡器,使用半隐式欧拉法在每个帧内进行 8 个子步骤(帧增量上限为 1/30 秒,这样后台标签页不会导致失控)。to(target) 重新定位目标而不重置速度,这使得快速点击感觉连续而非重新开始。set(x) 无动画跳跃,drag(x, v) 跟随指针并记录速度,kick(v) 注入脉冲。阻尼在每个节拍乘以全局 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% 的拉伸。始终将大小限制在正数最小值,因为欠阻尼弹簧可能低于零。
  • 按方向交换刚度。 在边缘对中,面向运动方向的边缘使用较硬的弹簧。如果没有交换,指示器将正确地向一个方向拉伸,而向另一个方向压缩。
  • 返回时采用相反的时间。 如果打开时宽度领先于高度,则在关闭时让高度领先于宽度。形状因此走一条不同的路径,这被解读为弹性而非倒放。
  • 状态变化用弹簧,形状重要的路径用时基曲线。 抛射物体需要可预测的弧线(二次贝塞尔,缓入缓出进度,约 800 毫秒)。独立的 x/y 弹簧会产生漂移路径。无论哪种方式,都要从逐帧速度推导出拉伸。
  • 保持内容正直。 要沿任意方向拉伸某物而不旋转其图片或文本,使用 rotate(a) scale(1+s, 1−s·0.45) rotate(−a)。
  • 每个组件一个大胆的动效。 组件的其余部分保持安静:颜色变化、短淡入淡出。
  • 空闲时平静。 没有东西自己摆动。最多在页面加载时安排一个协调的时刻(展示在 900 毫秒后打开动态岛一次)。

使其看起来更好(构建中的经验)

第一个加入购物车版本沿两个独立弹簧飞行了一个纯色球,将按钮缩小成一个空的紫色椭圆,并压扁了购物车。用户说它不好看。修复方案具有通用性:

  • 移动真实物体。 克隆产品图片,稍微抬起并带阴影,然后将其缩小成一个圆形芯片,在飞行过程中仍显示图片。抽象的圆点读起来像调试输出。
  • 中间形状必须承载意义。 缩小的按钮是一个空块,除非它显示了一些东西:一个旋转器、一个自绘的勾选、一个计数。添加按钮现在变成一个绿色圆圈并打勾,然后成长为 − n + 步进器。
  • 控件上的微妙体积。 按钮上 ±18% 的垂直凸起看起来像药丸形的蛋。±7% 读起来像柔软材料。
  • 到达需要一个接收器。 购物车图标下沉并倾斜,一个环脉冲向外扩散,徽章弹出。有些东西必须明显地抓住被抛出的东西。
  • 不要裁剪状态标记。 变形中的 overflow:hidden 药丸内的徽章会被裁剪;将其放在裁剪区域外的包装器中。
  • 不要重复信息。 购物车药丸显示“3 ชิ้น 195 บาท”,旁边还有一个徽章显示 3。现在只显示总数。
  • 以固定比例绘制插图。 平铺中的百分比定位 CSS 形状在平铺变宽时会拉伸。使用带有 viewBox 的內联 SVG,在平铺中居中。

可访问性和健壮性检查清单

  • 尊重减少动效(核心已实现;确保 CSS 关键帧和过渡也在 prefers-reduced-motion 媒体查询中被禁用)。
  • 使用真实的 button、input、role="switch"、role="tab"、role="slider" 等,并带有正确的 aria-* 状态,在与启动动画相同的调用中更新。
  • 被变形隐藏的内容应添加 inert(并且不得拦截点击:给不可见图层设置 pointer-events:none,然后在可见状态时将其重新打开)。
  • 当焦点控件消失到变形中时,有意识地移动焦点:到替换控件,在对话框关闭后回到触发器,并且仅在元素再次可见之后(visibility:hidden 元素不能接收焦点)。

相关技能