Skip to content

Completion compares the pass counter with strict equality, so an overshot counter loops forever #3213

Description

@Gamote

advanceTime decides completion with:

if (!this.loop || this.playCount === this.loop) {

Because the comparison is exact equality, any state where playCount is already above loop can never complete, and the animation loops forever.

The counter overshoots through the public API alone: with loop: true, every pass runs this.playCount += 1 unbounded. Calling setLoop(2) after four passes then asks a counter sitting at 4 to equal 2:

const anim = lottie.loadAnimation({
  container,
  renderer: "svg",
  autoplay: true,
  loop: true,
  animationData: shortAnimation,
});
// after four loopComplete events:
anim.setLoop(2);
// never completes: playCount is 4, and only playCount === 2 would stop it

The reverse path has the same shape with the mirrored guard (this.playCount-- counts down and the check is against 0), so a counter pushed past either bound is permanently unstoppable.

Comparing with >= in the forward branch (and the symmetric fix in the reverse one) makes an overshot counter complete at the next boundary instead.

Related: #3214, where an untyped setLoop value reaches the same comparison.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions