跳到主要内容

插画交互动画

插画的文档是 分层的 <g>(角色、控件、装饰)。动画改的是这些组的 transform / opacity / path,不是时间轴上的位图序列。世界单位是 illustrator 导出的 viewBox;嵌入页里的 CSS 盒子是 viewport。产品 UI 动效用 CSS 或 WAAPI;不要 把 SMIL 写进要消毒的用户 SVG。

1. 场景怎么切

静态层:背景、不交互装饰(可整层 <img> 或内联)
交互层:按钮热区、可 hover 的部件(必须是节点)
动效层:同上节点,用 CSS / WAAPI 改 transform
时间轴(可选):自己的 clock,不要依赖 SMIL begin

必须是节点:要 hover、focus、读屏、可点的部件。热区用透明 rect/path 盖在画面上,比把整张插画切成碎 path 更稳。

可以光栅:循环装饰(粒子感背景)、模糊光晕、导出 GIF/MP4。滤镜动画会强制光栅,层数一多就掉帧——能 CSS opacity 就不要 feGaussianBlur 每帧变 stdDeviation。

export function playReveal(el: SVGGraphicsElement, ms: number): Animation {
el.style.transformBox = "fill-box";
el.style.transformOrigin = "center";
return el.animate(
[
{ transform: "scale(0.96)", opacity: "0" },
{ transform: "scale(1)", opacity: "1" },
],
{ duration: ms, easing: "cubic-bezier(0.2, 0.8, 0.2, 1)", fill: "forwards" },
);
}

transform-box: fill-box 让缩放绕图形自己,而不是绕 SVG viewport。路径描边用 stroke-dasharray / stroke-dashoffset 或 WAAPI 同样属性;这够做「画出线」,不够当通用动画引擎。细节见 CSS 与 WAAPI路径描边

热区几何必须写在插画的用户空间里,和画面共用 viewBox。响应式只改 CSS 盒子与 preserveAspectRatio,见 响应式

2. SMIL 边界

animate / animateTransform / animateMotion 三引擎仍能跑,适合 自包含 .svg 文件(当图或独立文档)。应用 UI 不要依赖它:暂停、反向、和指针手势编排弱于 WAAPI,时间轴也难和自己的 clock 对齐。见 SMIL

用户上传文件里的 SMIL 按 消毒 默认剥掉。它既是动画,也是历史脚本宿主。

手段用在哪
CSS transition / @keyframeshover、主题、简单入场;能声明就不进 JS
WAAPI要播放头、反向、和手势衔接
SMIL只留在独立 .svg 资源里

可点的插画必须 inline。当 <img> 没有交互树,要点就叠 HTML overlay,位置仍跟世界走,见 Overlay

滤镜只给静态层。动画层用 transform / opacity,避免 滤镜重绘 每帧光栅整张插画。

适用:营销页、空状态、可点的角色部件。产品控件(按钮、开关)用 HTML + CSS,不要用插画 path 冒充控件。

3. 不适用

  • 用 SMIL 做产品状态机(开/关、步骤条):用数据和 CSS class。
  • 每帧 JS 改几百个 d:那是动画系统不够,或该预计算 sprite。
  • 把 Lottie 当 SVG 文档编辑:Lottie 是自己的时间轴运行时,导出 SVG 只是一种投影。

4. 失败形态

症状原因
hover 区域和画面错位热区画在屏幕空间,插画 viewBox 一响应式就漂
动画卡滤镜 + 大图每帧光栅;或 JS 逐属性打整棵树
消毒后不动了动效写在用户 SMIL 里,白名单杀掉是正确的
缩放后旋转轴跑偏没设 transform-box / origin
<img> 引用后点击无效图模式没有交互树,要 inline 或 overlay
入场动画播完控件点不了动画元素 pointer-events: none 没还回去,或热区被透明层盖住

权威资料

核对日期:2026-08-26