juliangarnier / anime

JavaScript animation engine
https://animejs.com
MIT License
50.4k stars 3.68k forks source link

Fix animation start on hidden document with suspendWhenDocumentHidden=false #826

Closed Ihor0k closed 2 years ago

Ihor0k commented 2 years ago

When play() is called on an inactive tab, the code execution stops at line #851 because in most browsers requestAnimationFrame is paused when the tab is inactive. Then animation starts only when switching back to the tab. The fix sets startTime to performance.now() if play is called on hidden document. It allows to resume an animation from the place where it must be when switching back to the tab.

Codepen with the issue: https://codepen.io/Ihor0k/pen/PoRVXWR

Ihor0k commented 2 years ago

I've realized that this fix would still not fulfill finished promise before switching back to the tab.

For those who iterested, I've used this workaround to fake animations while tab is not active:

function runAnimation(animation): Promise<void> {
  let timeoutId: number | null = null;
  let raf: number | null = null;

  function useRequestAnimationFrame() {
    if (timeoutId) {
      clearTimeout(timeoutId);
      timeoutId = null;
    }
    function loop(t) {
      animation.tick(t);
      raf = requestAnimationFrame(loop);
    }
    raf = requestAnimationFrame(loop);
  }

  function useTimeout() {
    if (raf) {
      cancelAnimationFrame(raf);
    }
    animation.tick(performance.now());
    timeoutId = setTimeout(() => {
      animation.tick(performance.now());
    }, animation.duration - animation.currentTime);
  }

  function run() {
    if (document.hidden) {
      useTimeout();
    } else {
      useRequestAnimationFrame();
    }
  }

  document.addEventListener("visibilitychange", run);
  run();

  return animation.finished.then(() => {
    if (timeoutId) clearTimeout(timeoutId);
    if (raf) cancelAnimationFrame(raf);
    document.removeEventListener("visibilitychange", run);
  });
}