Node.js中使用canvg渲染动态SVG:如何触发下一帧绘制?
Node.js 中使用 canvg 渲染动态 SVG 动画的解决方案
问题核心
canvg 在浏览器端依赖 requestAnimationFrame 驱动动态 SVG 的帧更新,但 Node.js 环境没有这个 API,导致调用 v.start() 或 v.render() 只能渲染动画首帧,无法推进后续帧的进度。需要手动模拟时间流逝,通过定时器结合时间差来驱动动画帧更新。
实现方案
核心思路是手动跟踪每帧的时间差,将时间差传入 canvg 的渲染方法,让它根据实际时间推进动画进度,替代浏览器端的 requestAnimationFrame 机制。
修改后的完整代码
const preset = presets.node({ DOMParser, canvas: Canvas, fetch, }) const canvas = preset.createCanvas(width, height) const ctx = canvas.getContext('2d') const svgStr = getSVG(t, 'Thank you') const v = Canvg.fromString(ctx, svgStr, preset) // 记录上一帧的时间戳,用于计算时间差 let lastTime = Date.now() async function drawFrame() { const currentTime = Date.now() // 计算当前帧与上一帧的时间差(单位:毫秒) const deltaTime = currentTime - lastTime lastTime = currentTime // 传入时间差,让canvg推进到对应动画进度 await v.render(deltaTime) // 导出当前帧的位图数据 const renderedImage = ctx.getImageData(0, 0, width, height) const buffer = Buffer.from(renderedImage.data) em.emit('newFrame', buffer) } // 用递归setTimeout实现动画循环,避免setInterval的累积延迟问题 function animate() { drawFrame().then(() => { // 模拟60fps的帧率,可根据需求调整间隔 setTimeout(animate, 1000 / 60) }) } // 启动动画 animate()
关键细节说明
- 放弃
v.start():v.start()内部会尝试绑定requestAnimationFrame自动循环,Node.js 下无法生效,因此改用手动调用v.render(deltaTime)来更新每一帧。 - 时间差计算:通过
lastTime和currentTime的差值,确保动画进度与实际时间流逝同步,不会因为渲染延迟导致动画速度异常。 - 定时器选择:递归
setTimeout比setInterval更可靠——setInterval不管前一帧是否完成都会触发,可能导致帧堆积;递归setTimeout会等上一帧渲染完成后再安排下一帧,更符合动画渲染逻辑。 - 帧率调整:示例中使用
1000/60作为间隔,模拟浏览器 60fps 的标准帧率,可根据实际需求修改(比如降低帧率减少资源消耗)。
内容的提问来源于stack exchange,提问作者Esben von Buchwald
相关产品推荐
相关产品推荐

