动画引擎原理 总览待审核
descriptor · mini-Lottie · 动画即数据

动画即数据:把 timeline 序列化成 descriptor

前面各页都是「写代码 animate(el, …)」的命令式动画。换一种范式:把动画写成一份纯数据 (descriptor),再写一个解释器读它、用引擎播出来。这正是 Lottie 的本质—— 它不是「更复杂的 SVG path 动画」,而是把 关键帧这套「时间→值」的 IR 序列化成 JSON, 再交给运行时渲染器逐帧插值。本页用 @vega/anim 手写一个最小版,并演示「动画是数据」带来的红利。

descriptor:把动画写成数据

一份 descriptor 描述的不是「画一条什么 path」,而是有哪些图层、每个图层的哪些属性、在哪些帧取什么值: layers[] 每层一个图形 + 若干属性轨 (tracks);每条轨是一串关键帧 { f, v, ease } (帧号、值、入此段的缓动)。单帧轨 = 静止值。下方的图形完全由右侧这份数据驱动—— 改任意一个数字再「重新载入」,动画随之改变。

frame 0 / 120 ·

descriptor (可编辑 —— 改完点「重新载入」)

解释器:把 descriptor 编译成一个 Animation

解释器只做两件事:其一,把每条属性轨喂给 createKeyframes(values, { times, ease }) 得到一个采样器—— 帧号按总长归一化成 times (落在 0→1),逐段缓动给到 ease;其二,建一个Animation 当播放头,它的 onUpdate(p) 把进度 p 喂给每条轨的采样器, 合成 transform / opacity 写到对应图层。整页就这点代码:

@vega/anim — 三十行把 descriptor 编译成动画

  

字段与引擎原语逐项对得上——descriptor 不过是这套引擎模型的序列化形式:

descriptor 字段含义@vega/anim 原语
tracks.x = [{f,v,ease}, …]一条属性的关键帧轨createKeyframes(values, { times, ease })
关键帧的 ease入此段的缓动曲线逐段 ease 数组 (长度 = 段数)
fps + duration (帧)总时长Animation.duration = 帧 / fps × 1000
多个 layers[]独立图层各跑各的轨AnimationonUpdate 内分发给每层
整体播放 / 变速 / 跳转播放器能力play / speed / seek / reverse 现成
动画是数据,于是免费得到三样东西:其一,可被程序改写——「换色」只是改 descriptor 里 shape.fill 一个值再重编译,编辑器里改任意数字亦然;其二,可 seek / 反向 / 变速—— 因为整份数据编译成一个Animation,而「时间→值」是纯函数, 拖 seek 条、点反向、调 speed 全部白送 (Lottie 的 setSpeed / playSegments 同理); 其三,矢量且轻——数据里没有像素,任意分辨率不糊。与浏览器原生及 Lottie / WAAPI 的能力边界对照见 与原生对照

每条轨的值如何插出来见插值,逐段快慢由缓动塑形, 多轨多层如何排在一条轴上见关键帧与编排;真实工业格式的细节 (After Effects → Bodymovin 导出、dotLottie 打包) 见CSS 之外的动画做法