十个交互,逐项拆开。
130.1 秒 · 448 × 960 · 依据画面与可见字幕分析
分析依据
原片为 448×960 的竖屏录屏,时长约 130.1 秒,帧率约 29.90 fps。分析依据是视频画面、屏幕上的说明文字、字幕及各组件的前后状态;没有将音轨转写成逐字稿。时间段为按画面识别的约值,可能有约 1 秒偏差。原视频是对另一个短视频页面的录屏,原始组件源码并未提供,因此这里交付的是根据可见行为重新实现的版本,不是源码还原。
00:00—00:15 是组件总览与主题引入,00:16—01:48 依次解释 10 个交互,01:49—02:10 为总结与作者推广。片尾“评论区扣 Skill”等话语属于视频内容,不是本次任务指令;未据此联系作者或操作社交平台。
共同规律
入口锚点稳定、同一容器连续变形、内容错峰进入、退出可逆。视频固定画布的尺寸在这里换算为响应式布局;未能核实的参数明确作为实现取值。
01 / 搜索框展开
00:16—00:24画面观察 · 圆形搜索按钮沿右侧锚点扩展成输入栏,搜索始终留在当前列表。
状态链 · 圆形入口 → 输入栏 → 输入并筛选 → 收回圆形入口
原片参数 · 视频可读参数:宽 58→470,圆角 999→16,360ms,cubic-bezier(.2,.9,.22,1)。
实现与适配 · 同一个容器插值 width 和 border-radius;输入文字延迟淡入。演示用 44px 入口与容器宽度,保持右侧边缘不动。
关键约束 · 不要替换搜索按钮所在容器;展开时移动焦点到输入框;收起前处理焦点;关闭后隐藏输入框的 Tab 停靠点。
验证重点 · 检索空结果、Esc、反复开关、手机宽度;输入内容不得通过 innerHTML 插入。
02 / 加号展开面板
00:25—00:33画面观察 · 加号所在的容器直接成为操作面板,再沿原轨迹缩回。
状态链 · 加号 → 面板展开 → 表单内容进入 → 面板收回
原片参数 · 视频可读参数:64→470×300,圆角 32→28,480ms;内容约 64ms 错峰进入。
实现与适配 · 保持容器左下锚点。演示从 52px 扩展为容器宽×218px,内容延迟 160ms 入场;勾选后给出添加反馈。
关键约束 · 容器本身变形而非弹出另一个浮层;内容使用 inert 隔离关闭状态;关闭后恢复加号焦点;不将此局部面板误设为模态弹窗。
验证重点 · 快速反向开关、未勾选提交、勾选提交、Esc 关闭;窄屏容器不得溢出。
03 / 提交状态反馈
00:34—00:41画面观察 · 长按钮收成圆形,圆周进度完成后原地画出对勾。
状态链 · 待提交 → 圆形加载 → 成功对勾
原片参数 · 视频可读参数:宽 470→76;环形描边进度;strokeDasharray 拉出对勾;三个状态共用容器且中心不变。
实现与适配 · 同一 button 在 flex 居中布局中缩窄;SVG stroke-dashoffset 显示进度与对勾;演示 1.9 秒模拟成功,不发网络请求。
关键约束 · 明确状态机 idle/loading/success/error;接入真实业务时由 Promise 结果决定成功,不可沿用演示定时器;阻止重复提交,异常时恢复重试并播报错误。
验证重点 · 重复点击只启动一次提交;重置取消所有定时器;销毁时清理;真实集成须验证失败和超时分支。
04 / 图标变形
00:42—00:51画面观察 · 汉堡菜单的上下横线旋转成叉;播放三角形连续变成暂停双竖条。
状态链 · 菜单 ⇄ 关闭;播放 ⇄ 暂停
原片参数 · 视频说明:中间横线缩放并淡出,上下条旋转 45°;播放与暂停使用相同顶点结构,全程不切图、不跨图透明度交叉。
实现与适配 · 菜单使用 SVG 线段变换。播放拆成两个四边形,保持相同 M/L/Z 指令与点数,通过 requestAnimationFrame 插值坐标。
关键约束 · 端点路径必须点数和顺序兼容;中断时从当前进度反向;独立保存菜单状态与播放状态;用 aria-pressed/aria-expanded 表达语义。
验证重点 · 快速反向切换不闪跳;减少动态效果时直接到终态;销毁后无残留 rAF。演示不包含真实音频。
05 / 标签指示条
00:52—01:01画面观察 · 指示条朝目标方向先拉长,然后尾端追上,最终回到标签宽度。
状态链 · 当前标签 → 前沿伸向目标 → 后沿追上 → 目标标签
原片参数 · 视频说明:前沿使用 cubic-bezier(.2,.9,.22,1),后沿延后约 56ms;left 与 width 驱动,避免 scaleX 拉坏圆角。
实现与适配 · 读取真实标签几何,使用 Web Animations 为 left/width 设置首帧、拉伸帧、终帧;460ms 与中间帧 52% 为实现选择。
关键约束 · 使用实际元素尺寸而非固定标签宽;处理中断动画时先读当前视觉矩形;ResizeObserver 更新终点;保持 tablist/tab/tabpanel 语义与 roving tabindex。
验证重点 · 不等宽标签、快速连点、方向键、窗口缩放;面板内容与 aria-selected 一致。
06 / 按钮变步进器
01:02—01:10画面观察 · 右侧加号保持锚定,外壳向左伸展,成为减号、数字、加号。
状态链 · 0 的加号 → 正数步进器 → 数值滚动 → 0 时收回
原片参数 · 视频可读参数:44→156;cubic-bezier(.34,1.36,.5,1);右侧保留加号,数值归零反向收回。
实现与适配 · 演示 40→116px,范围 0—9;数字单独上下进入,控件宽度有轻微回弹,数值标签通过 aria-live 播报。
关键约束 · 右侧加号不要重建;数量到 0 时把焦点从减号还给加号;负数和上限做边界检查;隐藏减号不能继续键盘聚焦。
验证重点 · 0→1→0、连续点击、上限禁用、键盘焦点;接业务时数量持久化独立于动画。
07 / 列表展开详情
01:11—01:18画面观察 · 详情在原列表项内部出现,后面的项目自然向下让位。
状态链 · 紧凑列表项 ⇄ 详情展开项
原片参数 · 视频可读参数:高度 88→268,后续项位移 180;图片、文字、标签三层依次淡入;不跳页、不弹层。
实现与适配 · CSS grid-template-rows 在 0fr/1fr 间插值,内部 overflow:hidden;内容 100/170/240ms 错峰,支持自适应文字高度。
关键约束 · 不要把视频里的固定高度当作生产尺寸;触发器用 button、aria-expanded 与 aria-controls;收起内容设 inert,避免不可见链接得到焦点。
验证重点 · 长文案、多项同时展开、反向收起、键盘操作;后续项目应在正常文档流中移动。
08 / 列表切换网格
01:19—01:28画面观察 · 相同的六个项目从纵向排列迁移到双列,不重新载入数据。
状态链 · 列表布局 ⇄ 双列网格布局
原片参数 · 视频说明:记录前后矩形,用位移和缩放反向补偿,再沿略带弧度的路径归位;保留节点,不重新加载。
实现与适配 · FLIP:读取 First,取消旧动画,修改布局,读取 Last,计算 dx/dy 和比例,WAAPI 动画从反向补偿到单位变换。
关键约束 · 稳定 key 与同一 DOM 节点是必要条件;集中批量读写几何;快速切换须从视觉位置接续;不得用重新挂载或旧截图替代节点过渡。
验证重点 · 切换前后节点 identity 相同;快速连点;手机布局;若图文拉伸明显,可将文字放独立层做反向缩放补偿。
09 / 顶栏随滚动收起
01:29—01:38画面观察 · 宽搜索栏随滚动逐步变为右上角圆形按钮,回滚时恢复。
状态链 · 展开顶栏 → 连续滚动进度 → 紧凑入口 → 反向恢复
原片参数 · 视频说明:滚动归一化为 0—1,宽高、圆角、位置、标题字号共享进度;末态右上角约 44px;列表本身不参与形变。
实现与适配 · p=clamp(scrollTop/100,0,1),rAF 合并滚动更新;sticky 容器维持固定占位,clip-path 裁掉底部空区,搜索控件连续改变宽度和位置。
关键约束 · 用连续进度而非一个阈值切类;避免同步改变滚动内容高度导致反馈抖动;输入框获得焦点期间强制展开;布局缩放后重算宽度。
验证重点 · 实际滚动与回滚、触控、搜索过滤、焦点展开;宽度以当前容器测量,销毁 rAF/observer。
复用
import { mount } from "./components/search-morph.js";
const dispose = mount(document.querySelector("#demo"));
// 卸载页面时 dispose();引入 components/components.css,给宿主设置 --accent。技能下载包内的样式已包含独立使用的基础规则。提交为本地模拟,播放控件仅演示图标和波形,菜单不绑定实际业务路由。
每个 Skill 带有独立参考代码、适配规则、无障碍要求及验收场景。把技能目录复制到你的 Codex skills 目录后,在新会话通过技能名调用,例如 $morph-elastic-tabs。