0

I have a node.js process with a canvas that is being exported to a frame buffer as a bitmap (RGBA values in Uint8Array).

Now I want to draw SVG to this canvas, and found canvg which works well for static SVG images so far.

But I also need to show animated SVGs. Currently I can only get the first frame of the SVG rendered on the canvas by using v.start() or v.render().

I understand that in a node.js environment we cannot call requestAnimationFrame() so that's probably why canvg does only render the first frame. It seems to work out of box on the basic example in a browser.

But I'm looking for a way to trigger the rendering again in a node.js process, based on timer or external event, with awareness for the time that has passed since last render, in respect for the animation progress.

  const preset = presets.node({
    DOMParser,
    canvas: Canvas,
    fetch,
  })

  const canvas = preset.createCanvas(width, height)
  const ctx = canvas.getContext('2d') // TODO: switch to RGB24

  const svgStr = getSVG(t, 'Thank you')
  const v = Canvg.fromString(ctx, svgStr, preset)
  v.start()
 
  async function drawFrame() {

    // I need something here to request the next frame of
    // the animation so it can be drawn on the canvas before
    // the canvas is exported as bitmap.
    // Example of what I need:
    await v.nextFrame(new Date().getMilliseconds())
   
    const renderedImage = ctx.getImageData(0, 0, width, height)
    const buffer = Buffer.from(renderedImage.data)
    em.emit('newFrame', buffer)
  } 

  setInterval(drawFrame, 200)
Esben von Buchwald
  • 2,772
  • 1
  • 29
  • 37

1 Answers1

0

The Node context has no UI, it's just the JS engine (with its own standard library API) so requestAnimationFrame would make no sense, there is no graphics context to signal that it has finished drawing a frame and is ready to draw something else now. Instead, you get process.nextTick(), which can schedule code to run "when the current synchronous code branch has completed".

For more details, see "The Node.js Event Loop, Timers, and process.nextTick()" and specifically the section on process.nextTick() use.

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
  • Thanks for your response. I'm completely aware that I have no directly connected display that can give me a callback when next frame will be rendered. That's why I want to use an external event for triggering the renderings, like I did with the `setInterval()` in the bottom. The only thing I ask for, is to make cansvg show the next frame of the animation every time I trigger the rendering of the canvas, so that I can rely on the animation features of SVG. – Esben von Buchwald Oct 14 '22 at 20:36
  • Right, and the way you do that is with process.nextTick, not setInterval, and then you check the actual time passed so you can control your frame rate (setInterval makes _no guarantee_ about when it triggers other than "at _least_ X milliseconds later" but if it's half an hour later, that's still 100% spec-compliant). In terms of getting the actual next frame, probably file an issue over on https://github.com/canvg/canvg/issues, since [the API](https://canvg.js.org/api/classes/Canvg) does not appear to have a `next()` or `next(timestamp)`. – Mike 'Pomax' Kamermans Oct 15 '22 at 01:23
  • Thanks for elaborating the timer/tick thing in node JS :) I guess I'll file an issue at canvg and hope for the best then. – Esben von Buchwald Oct 15 '22 at 18:29