javascriptweb-animations

Why does sharing a KeyframesEffect with two Animations cause one animation to never end?


In the following example, one animation will complete, and the other will keep going (because playState is not equal to finished):

const keyframes = new KeyframeEffect(null, [
    { // from
        opacity: 0,
    },
    { // to
        opacity: 1,
    }
], {duration: 2000, easing: 'ease'})

{
    const anim = new Animation(
        keyframes,
        document.timeline
    )

    anim.play()

    requestAnimationFrame(function loop() {
      console.log('1', keyframes.getComputedTiming().progress)
      if (anim.playState != 'finished') requestAnimationFrame(loop)
    }, 100)
}


{
    const anim = new Animation(
        keyframes,
        document.timeline
    )

    anim.play()

    requestAnimationFrame(function loop() {
      console.log('2', keyframes.getComputedTiming().progress)
      if (anim.playState != 'finished') requestAnimationFrame(loop)
    }, 100)
}

Why does one animation never get "finished"?


Solution

  • AnimationEffects, of which KeyframeEffect is a subclass, cannot be shared between Animations. This is because they rely on various state associated with their Animation.

    (In particular, they need to know if they are associated with a transition / CSS animation in order to stack correctly, and they need to know their animation's playback rate in order produce the right value at the ends of their active interval.)

    As a result, if you try to associate an AnimationEffect with an Animation, it is removed from any Animation it was previously associated with. This is specified in the procedure to set the associated effect of an animation.

    Incidentally, this is why the KeyframeEffect interface has a "copy constructor" so that you can clone KeyframeEffects.

    So in your example you could address this using:

    const anim = new Animation(
      new KeyframeEffect(keyframes),
      document.timeline
    )
    

    Now, as for why one of the animations never finishes, that appears to be a bug in Chrome. In Firefox the first animation (which lost its AnimationEffect) will terminate almost immediately after starting because its associated effect end becomes zero.