在前端工程与设计系统的协同演进中颜色Colors与间距Spacing的自动化同步早已被大部分团队攻克。然而一旦涉及动效设计变量Motion Design Tokens——涵盖复杂的贝塞尔缓动曲线Easing Curves、物理弹簧刚度Spring Stiffness以及分层时长Duration Scales时许多团队又瞬间退回到了“刀耕火种”的原始时代动效设计师在 Figma Variables 或 Tokens Studio 中精心调试了一组张力十足的动态曲线交付给研发时却只能在飞书文档里发一张写着(0.16, 1, 0.3, 1)的截图各业务线的前端工程师在自己的项目里随心所欲地手动命名有的叫TRANSITION_FAST有的写成easeCustom1甚至直接在 CSS 中到处硬编码裸字符串更头痛的是维护灾难当品牌升级需要将全局核心缓动曲线微调 5% 时全公司 30 多个业务仓库的前端工程师要耗费数周时间手动搜索替换漏改、拼写错误层出不穷最终导致整个产品矩阵的交互手感极其割裂。为了彻底打通设计工具与代码仓库之间的最后一堵高墙我们在内部打造了一套基于 Figma REST API、W3C DTCG 标准与 Babel/TypeScript 抽象语法树AST的动效自动化编译流水线。设计师在 Figma 中发布变量更新后CI 机器人会在 3 分钟内自动化编译并发布带有严格类型定义与详尽 JSDoc 注释的 NPM 私有包。本文将全景拆解其架构设计与核心编译器实现。架构拓扑单源定义到多端类型消费整个动效代码生成体系的核心是贯彻**单源真理Single Source of Truth**原则[Figma 官方设计系统云端文件 (Design Tokens Variables)] │ ▼ (定时 Webhook 或 GitHub Actions 触发) [Figma REST API 抓取器 (/v1/files/:key/variables/local)] │ ▼ (标准化为 W3C Design Tokens JSON 规范) [W3C DTCG 标准动效数据结构] │ ▼ (核心: TypeScript AST 编译器) ┌─────────────────┴─────────────────┐ ▼ ▼ [dist/tokens.ts (纯常量实现)] [dist/tokens.d.ts (类型与 JSDoc)] - as const 冻结常量 - 严格字面量联合类型 - CSS / WAAPI 双格式导出 - 内嵌贝塞尔曲率画像注释 │ ▼ (自动化流水线门禁校验) [NPM 私有包发布 (company-ds/motion-tokens)]阶段一标准化 W3C DTCG 动效数据契约从 Figma 提取出的原始变量往往包含大量绘图软件私有的元数据编译器首先将其清洗归一化为标准的W3C Design Tokens Community GroupDTCG格式{ motion: { easing: { gentle: { $type: cubicBezier, $value: [0.16, 1.0, 0.3, 1.0], $description: 适用于模态弹窗与大面积抽屉的优雅减速曲线 }, energetic: { $type: cubicBezier, $value: [0.34, 1.56, 0.64, 1.0], $description: 带 1.25 倍超调的活泼弹跳曲线适用于微小徽章与点赞 } }, duration: { instant: { $type: duration, $value: 150ms }, moderate: { $type: duration, $value: 350ms }, deliberate: { $type: duration, $value: 600ms } } } }阶段二核心编译器手写基于 AST 构建强类型代码在 Node.js 中如果用简单的字符串模板拼接生成代码极易产生语法错误或格式缩进混乱。我们采用babel/types与babel/generator直接在内存中构建合法的抽象语法树AST自动化注入不可篡改声明as const与高品质 JSDoc 注释// compileMotionTokens.ts import * as t from babel/types; import generate from babel/generator; import fs from fs-extra; import path from path; export interface MotionTokenSource { easing: Recordstring, { $value: [number, number, number, number]; $description?: string }; duration: Recordstring, { $value: string; $description?: string }; } export function compileMotionAst(source: MotionTokenSource): string { const programStatements: t.Statement[] []; // 1. 生成 Header 警告注释 const headerComment * * 该文件由 CI 动效编译流水线自动化生成严禁手动篡改 * 数据源: Figma Design System / Motion Variables ; // 2. 编译 Easing 贝塞尔对象 const easingProperties: t.ObjectProperty[] []; for (const [key, item] of Object.entries(source.easing)) { const [x1, y1, x2, y2] item.$value; const cssCubicString cubic-bezier(${x1}, ${y1}, ${x2}, ${y2}); // 构建嵌套对象: { cubicArray: [...], css: ... } const propValue t.objectExpression([ t.objectProperty( t.identifier(coordinates), t.arrayExpression(item.$value.map(v t.numericLiteral(v))) ), t.objectProperty( t.identifier(css), t.stringLiteral(cssCubicString) ), ]); const property t.objectProperty(t.identifier(key), propValue); // 附加 JSDoc 注释 if (item.$description) { t.addComment(property, leading, * ${item.$description} \n * 贝塞尔参数: ${cssCubicString} , true); } easingProperties.push(property); } // export const MotionEasings { ... } as const; const easingsVar t.exportNamedDeclaration( t.variableDeclaration(const, [ t.variableDeclarator( t.identifier(MotionEasings), t.tsAsExpression( t.objectExpression(easingProperties), t.tsTypeReference(t.identifier(const)) ) ), ]) ); programStatements.push(easingsVar); // 3. 编译 Duration 时长对象 const durationProperties: t.ObjectProperty[] []; for (const [key, item] of Object.entries(source.duration)) { const rawVal item.$value; const msValue parseInt(rawVal, 10); const propValue t.objectExpression([ t.objectProperty(t.identifier(css), t.stringLiteral(rawVal)), t.objectProperty(t.identifier(ms), t.numericLiteral(msValue)), ]); const property t.objectProperty(t.identifier(key), propValue); if (item.$description) { t.addComment(property, leading, * ${item.$description} , true); } durationProperties.push(property); } const durationVar t.exportNamedDeclaration( t.variableDeclaration(const, [ t.variableDeclarator( t.identifier(MotionDurations), t.tsAsExpression( t.objectExpression(durationProperties), t.tsTypeReference(t.identifier(const)) ) ), ]) ); programStatements.push(durationVar); // 4. 导出自动推导的联合类型 // export type MotionEasingName keyof typeof MotionEasings; const easingTypeExport t.exportNamedDeclaration( t.tsTypeAliasDeclaration( t.identifier(MotionEasingName), null, t.tsTypeOperator( t.tsTypeQuery(t.identifier(MotionEasings)), keyof ) ) ); programStatements.push(easingTypeExport); // 5. 将 AST 序列化为标准的 TypeScript 源码 const ast t.program(programStatements); const output generate.default(ast, { retainLines: false }); return /*${headerComment}*/\n\n${output.code}\n; }阶段三编译产物实证与 IDE 智能体验经过该流水线生成的产物其类型严密性与开发者体验达到了前所未有的高度// 编译生成的 dist/motionTokens.ts 真实面貌 /** * 适用于模态弹窗与大面积抽屉的优雅减速曲线 * 贝塞尔参数: cubic-bezier(0.16, 1, 0.3, 1) */ export const MotionEasings { gentle: { coordinates: [0.16, 1, 0.3, 1], css: cubic-bezier(0.16, 1, 0.3, 1) }, energetic: { coordinates: [0.34, 1.56, 0.64, 1], css: cubic-bezier(0.34, 1.56, 0.64, 1) } } as const; export const MotionDurations { instant: { css: 150ms, ms: 150 }, moderate: { css: 350ms, ms: 350 }, deliberate: { css: 600ms, ms: 600 } } as const; export type MotionEasingName keyof typeof MotionEasings; export type MotionDurationName keyof typeof MotionDurations;业务端消费的无缝快感前端工程师在组件中编写动效时享有百分之百的 IDE 强类型自动补全与 JSDoc 悬浮提示// Dialog.tsx import { MotionEasings, MotionDurations } from company-ds/motion-tokens; const style { // 悬浮在 gentle 上直接显示设计规范里的说明与贝塞尔参数 transition: transform ${MotionDurations.moderate.css} ${MotionEasings.gentle.css}, };如果工程师不小心拼错了一个单词如写成MotionEasings.gentllTypeScript 编译器在代码尚未提交前就会立即红线报错彻底扼杀了拼写错误流入线上的可能CI/CD 质量防护破坏性变更拦截Breaking Changes Guard在自动化流水线中如果设计师在 Figma 里不小心删除了某个历史变量直接发布 Minor 版本会导致全公司所有正在使用该变量的项目在打包时挂掉。我们在 GitHub Actions 中配置了AST 语义比对门禁每次收到更新事件时下载上一版本已发布的company-ds/motion-tokens使用 TypeScript Compiler API 解析两个版本的 AST 类型声明如果检测到已有键名被删除或重命名强制将版本号推升为Major主版本并在企业微信群中发出强行阻断报警要求研发与设计代表共同签署变更准入。结语让动效成为坚固的软件契约动效从不应该是一过性的感性猜测它是构建数字世界物理秩序的刚性契约。通过将 Figma 中的矢量变量自动编译为基于 AST 的 TypeScript 强类型常量包我们彻底打破了设计与研发之间长达数年的语义代沟。当设计师在 Figma 画板上轻轻拖拽调整控制点的瞬间数千行严格自洽的高性能代码已经在 CI 服务器中静静编译成形为亿万用户面前的每一次界面起伏注入了精确恒久的现代工程品质。