svg矢量动画帧设计依赖javascript手动控制属性变化,以requestanimationframe为定时器核心,通过起始时间、持续时间和缓动函数计算插值状态,优先使用transform和stroke-dashoffset优化性能,并支持多动画协同与时间轴控制。

SVG矢量动画帧设计不依赖CSS动画或SMIL(已逐步弃用),而是通过JavaScript控制SVG元素的属性变化,配合定时器实现逐帧或插值动画。核心在于手动更新状态 + 定时驱动 + 渲染同步,关键不是“画多少帧”,而是“怎么精准更新”。
选择合适的定时器机制
避免使用 setTimeout 递归模拟帧循环(易漂移、难控制),优先采用 requestAnimationFrame(rAF)——它由浏览器调度,与屏幕刷新率同步(通常60fps),渲染更平滑且节能。
- rAF 自动适配设备刷新率,比
setInterval(16)更可靠 - 不要在 rAF 回调里直接修改大量 DOM 属性;先计算状态,再批量写入
- 需暂停/恢复动画时,用
cancelAnimationFrame管理 ID,而非清除定时器
管理动画帧状态与插值逻辑
每一帧的本质是:根据当前时间戳,算出目标属性值(如 cx、transform、stroke-dashoffset),再赋给 SVG 元素。推荐封装一个轻量帧控制器:
- 记录起始时间(
startTime)、持续时间(duration)、缓动函数(如t => t * t) - 在 rAF 回调中计算已过比例
t = Math.min((now - startTime) / duration, 1) - 用插值公式生成当前值:例如从
x=0到x=200,当前x = 0 + 200 * ease(t) - 直接设置 SVG 属性:
circle.setAttribute('cx', x.toFixed(2))
优化 SVG 渲染性能
频繁操作 SVG 属性可能触发重排重绘。几个实用技巧:
- 优先用
transform(如translate(x,y))代替修改x/y或cx/cy,GPU 加速更友好 - 对路径动画(如描边绘制),用
stroke-dasharray+stroke-dashoffset控制“露出长度”,比 path 重绘高效得多 - 复杂图形可预先定义
<defs></defs>中的<g></g>或<symbol></symbol>,动画时只改实例的use元素 transform - 避免在动画循环中查询
getBBox()或getComputedTextLength(),这些是强制同步布局的“重操作”
支持多动画协同与时间轴控制
单个 rAF 主循环可驱动多个动画实例,只需统一时间基准:
- 每个动画对象保存自己的
startTime和duration,共享同一 rAF 时间戳 - 用简单状态机管理播放/暂停/重播:例如
anim.state = 'playing',rAF 中跳过暂停项 - 需要精确时间轴(如 SVG 图标序列帧),可用
currentTime映射到帧索引:frameIndex = Math.floor(currentTime / frameDuration) % totalFrames,再切换<g></g>的 visibility 或 use 的 href











