rive-wasm icon indicating copy to clipboard operation
rive-wasm copied to clipboard

APIs for integrating into existing event loop

Open arthuro555 opened this issue 2 years ago • 3 comments

Description

The Rive Advanced API requires that one uses rive's requestAnimationFrame. However, that is not practical for an HTML5 game engine:

  • All games need the game loop, not all games require rive animations. Thus we need to either override requestAnimationFrame to rive's globally, or to always include rive in builds even when it is not in use.
  • We may not be using requestAnimationFrame: when the window is not focused, requestAnimationFrame does not trigger, but some games require to continuously run, so we may for example switch to setTimeout during that time to ensure the game logic keeps on running, and rive might break there.
  • Generally it requires integrating rive more deeply into the core of the engine than it needs to be (when it could just be an extension)

Proposed solution

I am assuming rive is only doing this as a hand-holding way to ensure some code is called before and after rendering. However, since that is supposed to be the "Low level" API, it is expected either way that the API might not be convenient or hand-holding but in exchange allows complete power and flexibility.

Therefore, I think it would be great to be able to do something like rive.startFrame()/rive.endFrame() as an alternative (or full replacement) to rive's requestAnimationFrame

arthuro555 avatar Nov 02 '23 14:11 arthuro555

Hi! You're right in that we're creating a simple wrapper around the requestAnimationFrame function, mainly as a way to coordinate rendering calls that are deferred. This helps coordinate certain scenarios such as Rive assets with mesh's, where there's an offscreen context we draw to first before other main canvas2d draw calls. The rive-wrapped rAF function calls a (not-exposed) function called flushCanvasRenderers that helps coordinate that sequence.

If we exposed this flushCanvasRenderers function, you could certainly use your own game's rAF loop and then before calling the next frame, call this new exposed function to draw Rive content. And in the case of you switching to setTimeout, you should still be able to advance the artboard and stateMachine / animation by passing some elapsed time, however you want to track that, as you normally would within the rAF loop. Would this help with your integration?

Example of what this might look like:

function draw(time) {
    if (!lastTime) {
      lastTime = time;
    }
    const elapsedMs = time - lastTime;
    const elapsedSeconds = elapsedMs / 1000;
    lastTime = time;

    renderer.clear();
    if (artboard) {
      if (stateMachine) {
        stateMachine.advance(elapsedSeconds);
      }
      artboard.advance(elapsedSeconds);
      renderer.save();
      renderer.align(
        rive.Fit.contain,
        rive.Alignment.center,
        {
          minX: 0,
          minY: 0,
          maxX: canvas.width,
          maxY: canvas.height,
        },
        artboard.bounds
      );
      artboard.draw(renderer);
      renderer.restore();
    }
    // Draw Rive contents
    rive.flushCanvasRenderers();
    requestAnimationFrame(draw);
  }
  requestAnimationFrame(draw);

zplata avatar Nov 14 '23 20:11 zplata

Yes, that sounds perfect. Thank you for your response!

arthuro555 avatar Nov 14 '23 20:11 arthuro555

Hi sorry for the delay here.. but we just introduced a similarly-named API into v2.10.0.

You can use your own rAF loop, just make sure to call rive.resolveAnimationFrame() at the end of the loop, like in the snippet a few messages up. We're updating docs right now to reflect the new API, but you should be able to try it out now

zplata avatar Feb 06 '24 21:02 zplata