Skip to content

How to reverse an infinate animation in Web Animation API?

In Web Animation API, we can animate elements by Element.animate interface. The returned Animation object can be played, paused or reversed by .play(), .pause() and .reverse().

let img = document.querySelector('#happy-boy');

const boy_animation = [
    { transform: 'rotate(0)' },
    { transform: 'rotate(360deg)' },
];
const boy_timing = {
    duration: 3000,
    iterations: Infinity,
}

let animation = img.animate(boy_animation, boy_timing);
animation.reverse(); // <-- fails with DOM exception

I get this error when i try to reverse() the animation:

Cannot play reversed Animation with infinite target effect end.

chrome 86 on Linux

Answer

The behavior of reverse(), like play(), is that if the animation is at the “end”, it jumps back to the start and begins playing.

For reverse() this means that if the current time is zero when you call it (as in your example), then it should jump back to the beginning. However, if your animation has an infinite length, that would mean jumping to infinity!

If you simply want to run the animation’s keyframes backwards, you can use the direction property.

For example:

animation.effect.updateTiming({ direction: 'reverse' });

Note, however, that unlike reverse(), updating the direction while an animation is in progress can cause it to jump position.

If you want to be able to change the animation direction backwards and forwards while it is in progress and make it repeat forever, you can either:

  1. Set a very long iteration count and start the animation in the middle of that range, or

  2. Use updateTiming({ direction: 'reverse' }) and adjust the currentTime so that it doesn’t jump. Something like the following might work:

     const ct = animation.effect.getComputedTiming();
     animation.currentTime =
         ct.currentInteration * ct.duration +
         (ct.duration - ct.localTime % ct.duration);
    

Note further that even using updateTiming as in (2) can cause the animation to jump a little if the animation is running asynchronously (e.g. most transform and opacity animations) since there can be some small lag between the timing on the main thread where your Javascript runs and the thread/process where the animation is being run.

Using reverse() as in (1) (or updatePlaybackRate()) avoids that problem since it synchronizes any async animations before updating them.