rive-app / rive-wasm

Wasm/JS runtime for Rive
MIT License
703 stars 51 forks source link

APIs for integrating into existing event loop #336

Open arthuro555 opened 11 months ago

arthuro555 commented 11 months ago

Description

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

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

zplata commented 10 months ago

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);
arthuro555 commented 10 months ago

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

zplata commented 8 months ago

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