图像与设计
从 Figma 设计实现动效代码时,如何用…
当需要把 Figma 设计中的动画/过渡落地为仓库中的动效代码(motion.dev、CSS keyframes 或框架动画库)时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
从 Figma 设计实现动效代码时,如何特定
解决思路
- 确认静态设计上下文可用:调用 get_design_context(fileKey=":fileKey", nodeId="<node-id>");若此前已对该节点调用过则复用其输出。把它作为结构依据——层级、尺寸、样式、资源、Code Connect 提示、截图上下文,以及它可能包含的动效放置标记。
- 获取权威动效数据:调用 get_motion_context(fileKey=":fileKey", nodeId="<node-id>", recursive=true)。响应中每个动画节点包含 codeSnippets(预生成的 CSS @keyframes 与 motion.dev 字符串,直接使用,不要从 fallback 数据重新生成)、keyframeBindings(仅在两种 snippet 都缺失时作为回退数据)、motionSummary(仅在没有 snippet 时存在,用它构建)、fallbackNodeId(可选的组件化匹配回退 id)。递归响应还包含顶层 timelineCohorts 数组,元素为 { rootNodeId, durationMs, loopMode: 'once' | 'loop' | 'boomerang', memberNodeIds[] }。
- 理解字段省略语义:当 snippet 存在时,motionSummary、timelineDurationMs、transformOrigin 可能被省略以缩小负载——snippet 已携带 duration 与 transform-origin,cohort 已携带 durationMs;字段缺失绝不代表没有动画。递归响应会去重完全相同的 snippet,被替换为指向首个相同动效节点的注释,应复用同一组件/变体/类/常量而非再写一遍。MCP 服务器根据 clientFrameworks 推断 CSS 还是 motion.dev snippet;若只返回一种格式,把它适配到用户技术栈,不要假设另一种格式失败。
- 合并静态与动效上下文:从 get_motion_context.nodes 出发(而非静态 JSX 中可见的 motion.* 标签),每个返回节点都是动画节点。先按精确 nodeId / data-node-id 匹配回 get_design_context;仅当无精确匹配时才尝试 fallbackNodeId / data-node-id;两者都失败后才用节点名/类型与截图位置。
- 精确 id 匹配优先于 fallbackNodeId:fallbackNodeId 指向 D2R 在可复用组件内部可能输出的底层组件 id,被该组件的每个实例共享。若精确 nodeId 存在于设计上下文中,就在那里应用动效并忽略 fallback。这对根实例动画至关重要:同一组件的不同实例可以有不同的旋转或位移,把动效应用到共享组件体会错误地动画所有实例。
- 按 data-node-id 把每个动效节点应用到匹配的设计结构上:匹配的 data-node-id 是结构锚点,不一定是最终接收动效的 DOM 元素。根据 snippet 形态与放置标记决定动效放在该元素本身、包装器、内部元素还是内联 SVG path 上。若设计上下文输出的是普通结构元素(div、p、span、组件根等)且 snippet 目标就是该元素,则转换为合适的 motion.{tag} 或添加 motion 包装器,同时保留节点的文本、子元素、类/样式、属性与 data-node-id。加载 references/examples-and-anti-examples.md 查看合并示例。
- 组件化子动效通常按 fallback 匹配:当设计上下文把 Figma 实例抽取为可复用 React 组件时,组件体内部子节点常带底层组件 id(如 4002:3957),而动效上下文报告的是实时实例 id(如 I4005:6111;30:8005)。此时用 fallbackNodeId 找到组件体的 data-node-id,但把动效限定在你正在实现的渲染实例上。若有多个实例且只有一个根动效不同,精确 id 匹配可保持该实例动效独立。
- 保留 display: contents 包装器——除非该组本身有动画:布局透明的组包装器会以 contents(Tailwind contents)出现,通常伴随无效的 absolute/inset-[…]。对静态组,保留 display: contents 并让子元素相对最近的真实祖先定位——把包装器的 inset 转成定位盒会把子元素重新挂到更小的盒子上,导致渲染过小/向内偏移。对动画组(组节点自身有动效),display: contents 无法承载 transform,应替换为真实定位包装器并在其上应用组动效。实现前加载 references/gotchas.md。
- 把 get_motion_context 视为完整的动画节点清单:有些动画节点渲染为普通(非 motion)元素——组件实例根(普通定位 div)、文本(p)、遮罩——但仍带 data-node-id。遍历动效响应中的每个节点,把其动效应用到匹配 data-node-id 的元素上,按需包装或转换。若某动画节点在输出中完全没有对应元素(例如动画遮罩被压平成静态 mask-image),不要静默丢弃——留下 // TODO: <nodeId> motion unsupported 注释并在总结中指出。
- 若某节点出现在动效上下文但不在静态 JSX 中,补上表示它所需的元素——设计上下文代码是参考,不是完整的动画清单。
- 当设计与动效上下文冲突(timing/easing/动画值)时,优先采用 get_motion_context。
- 路径级 SVG 动效:内联 SVG 并动画真实 <path>。当 get_motion_context 目标是矢量路径(PATH_TRIM、motion.path、stroke-dasharray)但设计上下文把它渲染为 <img> 时,内联 SVG 并把 snippet 应用到 <path>,保留布局包装器。加载 references/svg-and-path-motion.md 获取完整做法(motion.path、pathLength="1"、wrapper+path 分层、CSS path-trim)。
- 处理交错 transform:同时具有静态基础 transform 与动画 transform 的节点会被拆分到嵌套元素上,使两者正确复合而非互相冲突:一个无 id 的 motion.div 携带 data-motion-wrapper-for="<nodeId>"(外层包装器)包裹静态 transform div(如 rotate-45 + hypot() 尺寸——或包装器自身携带 data-motion-transform-template="<css>"),再包裹内层节点(data-node-id)。保持 wrapper > static-transform div > inner 的嵌套,折叠会破坏尺寸与基础 transform。
- 按 data-motion-keys 放置轨道:包装器的 data-motion-keys(transform 轨道——x/y/rotate/scaleX/scaleY/skewX)放在外层包装器上;内层元素的 data-motion-keys 放在内层元素上。
- 重新应用 data-motion-transform-template:若包装器携带它,设置 transformTemplate={(_, generated) => "<css> " + generated},使动画 transform 复合在该静态布局 transform 之上。
- 用静态基础偏移动画 transform(避免双重旋转):get_motion_context 给出的是节点的绝对 transform,已包含那些 div 应用的静态基础。在 rotate-45 基础上 rotate snippet 为 [45, 125, 125] 意味着包装器应动画偏移量 [0, 80, 80](= 绝对值 − 45),而非绝对值——否则 45° 会应用两次,静止时元素处于 90°。无静态基础的轨道(如从 0 开始的 x/y)原样传递。参见交错 transform 示例。
- 保持布局 transform 与 Motion transform 分离:对每个动画 rotate、scale 或 skew 的 motion.* 元素,确认它不依赖 Tailwind 布局 transform(如 -translate-x-1/2 或 -translate-y-1/2)做居中/定位。这些工具类与 Motion.dev 内联写入的 CSS transform 属性相同,Motion 的 transform 会抹掉布局 translate。若两者都需要,把元素拆成承载居中/定位 transform 的静态布局包装器与承载动画 rotate/scale/opacity 的内层 motion.* 元素,或在 Motion 中编码布局偏移(x: "-50%")并在每个关键帧都保留它。
- 在代码中应用动效:若 snippet 含 motion.dev,React 目标直接逐字使用,从 motion/react 导入——除非代码库已用其他动效库(Framer Motion、React Spring、GSAP),此时适配到该库。加载 references/framework-recommendations.md 以适配其他技术栈或选择库。若 snippet 含 CSS keyframes,用于 vanilla/非 React 目标,或代码库没有 React 动效库时。若无 snippet(仅 keyframe-bindings),用 keyframeBindings + motionSummary 构建等价的 motion.dev/CSS,循环时序取自 cohort 的 durationMs / loopMode,duration 与 transformOrigin 取自结构化字段。
- 验证:添加新导入前先阅读组件现有的动效导入/约定;若用户已用 Framer Motion / React Spring / anime.js,适配而非强推 motion.dev。在批量修改多个节点前,先端到端抽查一个动画(重载、观察、迭代)。加载 references/gotchas.md,其中覆盖 Figma 动效输出中见过的具体 bug 与边界情况,并在生成代码中修正这些情况。
- 遵守关键规则:尊重工具输出的值而非其布局——保留 codeSnippets 中精确的 timing、easing、关键帧值与 transformOrigin,snippet 存在时不要从 keyframeBindings 或结构化字段重新生成(重新生成会在自定义贝塞尔缓动、弹簧近似与过冲值上丢失保真度)。transformOrigin 是逐元素的:对每个缩放/旋转节点应用其自身的(包括嵌套缩放器,不只是外层包装器),否则元素会从默认中心旋转/缩放,从错误的角生长或旋转。但 snippet 是单个节点的数据,不是复制粘贴模板:当许多节点共享它时,按规则 7 提取复用而非粘贴 N 次。
- 匹配用户现有动效技术栈:添加依赖前阅读组件导入与同级动画。若用户已有 Framer Motion、React Spring、anime.js、GSAP,适配输出到其技术栈而非强推 motion.dev。
- 遵守 prefers-reduced-motion:任何添加的动效都必须在 @media (prefers-reduced-motion: reduce) 下减弱或禁用——通常跳过 animate(渲染初始/静止状态)或把时长降到接近零。这是无障碍默认要求,不是可选项。
- 批量前先端到端验证一个动画:构建、重载并观察一个完整时间线循环——确认每个动画节点在其关键帧轨道指定的时间出现。“无错误渲染”不等于“正确渲染”。批量时动效失败会叠加——一个节点上错误的缓动容易发现,二十个节点上同样的 bug 要花数小时理清。
- 不要捏造动效:若节点在响应中没有动效数据,保持静态。不要从设计其他地方借用缓动/时长默认值,也不要因为“组件其余部分有动画”就自动加动画。
- 不要为了 Read 而下载资源:get_design_context / get_motion_context 以 URL(/api/mcp/asset/...)返回资源,通常是 SVG。在消费方获取处直接引用 URL(<img src>、CSS background-image、资源导入),或 curl 一个以内联其内容(例如内联 SVG 并在 SwiftUI 上用 NSImage(data:) 渲染)。重要例外是路径级 SVG 动效:若动效 snippet 目标是 SVG 资源内的 path,应内联 SVG 并动画真实 path,而非留在 <img> 后面。不要把资源下载后喂给 Read 工具:SVG 不是可 Read 的图片格式,读取会被拒绝并浪费——且不识别 SVG 为图片的文件工具可能让循环卡住。
- 提取重复动效——绝不要逐元素复制粘贴 snippet:许多节点通常共享同一动画,仅 stagger 延迟、偏移或目标值不同。把共享动效实现一次——一个可复用动画组件或按变化值参数化的 variants 对象——从映射数组渲染(items.map(...)),并把重复字面量(时长、缓动数组、偏移)提取为具名常量。动画的值保持 snippet 逐字不变(规则 1);代码保持 DRY。同一 transition 对象粘贴 15+ 次(800 行本应 150 行)是低质量结果——保真度与可维护性都会被评分。
- 框架默认建议(当用户无现有技术栈时):React 用 motion.dev(motion 包),工具直接返回 motion.dev 代码,直接使用。Vanilla/非 React Web 用 CSS @keyframes 与 animation 简写,工具直接返回。SwiftUI 用原生 .animation(...) 修饰符,从 CSS snippet 翻译(get_motion_context 不输出 SwiftUI 代码,但 SwiftUI/iOS 客户端仍得到 CSS 格式;仅在无 snippet 时回退到 keyframeBindings / motionSummary / cohort)。只使用真实 SwiftUI API——没有修饰符直接接受 Figma/CSS 缓动,因此加载 references/framework-recommendations.md#swiftui-translation,把缓动映射到其 SwiftUI 等价物并验证而非臆造。此路径仍在演进,不确定时与用户确认。
- 对成熟效果类别,优先用库而非手写 CSS:玻璃/glassmorphism、彩带、粒子系统、基于物理的交互、滚动联动动效等有经过实战检验的库实现,比生成的关键帧更好地处理跨浏览器怪癖、无障碍与性能。加载 references/framework-recommendations.md 获取按效果类别的完整库表。把这些作为建议而非强制——由用户决定。
- 按需加载参考文件:references/examples-and-anti-examples.md(合并工作流、交错 transform、检查是否重建 DOM/交换节点位置/丢失 transformOrigin);references/gotchas.md(Figma 特有动效 bug 及修复:嵌套组的旋转/缩放原点、HOLD 缓动语义、CUSTOM_SPRING 保留、独立轴缩放歧义、颜色插值),并始终同时加载 references/motion-lint-rules.md;references/svg-and-path-motion.md(矢量路径动效);references/framework-recommendations.md(motion.dev、CSS keyframes、SwiftUI 默认、按效果类别的库表);references/unsupported-and-fallbacks.md(当前无法干净导出的 Figma 动效特性:文本动画、路径动画、遮罩/布尔、变体/过渡,含 video/lottie 回退指南),并始终同时加载 references/motion-lint-rules.md;references/motion-lint-rules.md(已知导出限制的错误与警告,必须向用户呈现)。
适用边界 · 注意事项
- 用户要求在 Figma 内部创建/编辑动画本身时——应改用 figma-use 技能。
- 没有连接并访问 Figma MCP 服务器,或无法从用户提供的 Figma URL 解析出 nodeId 时。
- 目标不是用户仓库中的动效代码,或需要更广泛的交互式变体流程、需要产品特定状态处理时。