动画引擎原理 总览待审核
path morph · d 插值 · 顶点对齐

形状形变 morph:path d 插值

两条 path 之间的平滑形变,本质仍是插值——只不过插的不是一个数,而是 d 属性里的一长串坐标。前提很硬:两条 path 的命令结构必须相同(命令类型与数量一一对应)。 满足这点后,把两条 path 的数值参数逐位线性插值,再拼回 d 字符串 setAttribute 到同一个 <path>,形状就会从一个连续地长成另一个。 本页第一部分的四个形状(circle / square / star / heart)都用同样数量的 C 曲线写出,顶点天然一一对应, 因此可以直接互相 morph;第二部分再处理命令结构不同的通用情形(重采样 + 对齐)。

目标 heart 进度 命令数

命令结构不同的形状之间(三角形 3 段直线、十字 12 段、blob 几段曲线),逐位插值就失效了。 通用做法是重采样:用 getTotalLength() / getPointAtLength() 把每个形状按弧长均匀采成同样多的点,任意 path 都化成等长点集,即可逐点插值。再做一次 旋转对齐(循环移位,让两组点的起点与走向最接近),避免 morph 时整体拧成麻花。 关掉「旋转对齐」就能看到不对齐时的扭曲。

目标 cross 采样点 对齐移位
morph 的前提:插值器要对位相加,两条 path 必须命令结构对齐——同样数量、同样类型的命令, 顶点数相同。这样 d 就退化成一个等长数字数组,与数字插值 完全同构,只是维度从 1 变成几十。本页的四个形状刻意都写成「1 个 M + 8 个 C」,数组长度恒为 50, 所以任意两者之间都能直接 morph。 长度不等怎么办:真实图标很少天生对齐——三角形 3 个顶点、五角星 10 个顶点,数组长度不同便无法逐位插值。 工程上需先重采样 / 对齐:在顶点少的一方插入冗余点(把一条直线段拆成多段、或把多个点叠在同一坐标), 使两侧命令数与类型一致;更复杂的拓扑(孔洞、分裂)则要拆分曲线并配对子路径。 flubber 正是做这件事的库: 自动添加点、最优配对、必要时把一个形状分裂成多个去匹配另一个。第一部分用等长形状规避了这一步; 第二部分则真正做重采样——按弧长把任意形状采成等长点集再循环移位对齐,因此三角形、十字、blob 这些命令数不同的形状之间也能 morph(代价是曲线被离散成折线,采样点越多越平滑)。
@vega/anim — d 数组逐位插值

  

若只想画出单条路径本身、或让物体沿路径行进,见SVG 路径动画; morph 把整段 d 当作高维向量来插,与基础的插值同源, 区别只在于此处需要一个「path 插值器」(逐位混合数字 + 拼回 d),而非单值的 mix