javascriptgoogle-chromeweb-animations

Incorrect animation progress when calling from setTimeout


I'm using Web Animations API to animate DOM elements. I want to display animation progress after 1 second.

This is what I've tried.

const animation = div.animate({
  opacity: 0
}, {
  duration: 2000
});
setTimeout(() => console.log(animation.effect.getComputedTiming().progress), 1000);

I expect 0.5 to be displayed because setTimeout function is invoked when the animation is halfway through. The problem is that 0.0083 is printed


Solution

  • Animations generated using animate() (or CSS animations/transitions for that matter) do not start immediately.

    Rather, browsers perform necessary set up work first, such as splitting the content into suitable layers, rendering those layers, passing the layers and animation information over to the compositor thread/process and then starting the animation (and resolving the ready promise from the Animation object that animate() returns).

    Because this setup cost is often non-zero (especially on lower-end devices), if browsers did not do this the animation would jump when it starts because by the time the browser finally got to display the animation the current time of the animation would already be past 0ms. e.g. if the browser took 100ms to set up the animation, the first half a dozen frames would be dropped and the animation would jump.

    (For what it's worth, one of the motivations behind will-change was to allow authors to pay this setup cost upfront so the animation can start more quickly.)

    All browsers perform this optimization but the precise timing varies from browser to browser and the setup cost will vary between devices and platforms. The Animation.ready promise is provided so you can wait until the animation has actually started regardless of the browser/platform/device.

    You can force the animation to start immediately, however, by setting the startTime directly. For example,

    const animation = elem.animate(...);
    animation.startTime = document.timeline.currentTime;
    

    This, however, may cause the first few frames of your animation to be dropped, particularly on lower-end devices.