goToAndStop with a marker name renders the wrong frame when ip or a segment is non-zero
goToAndStop and goToAndPlay read a marker's time in two different coordinate systems, so they cannot both be right.
goToAndStop's marker branch calls this.goToAndStop(marker.time, true). With isFrame: true the value lands in setCurrentRawFrameValue, and rendering draws currentFrame + firstFrame, so marker.time is treated as relative to the playable range. But marker.time is tm from the file, an absolute file frame, which is exactly how goToAndPlay's marker branch treats it: it passes the same value to playSegments, where adjustSegment assigns it to firstFrame, an absolute position.
Reproduction, with an in point of 20 and a marker at frame 30:
<div id="a"></div>
<script src="https://unpkg.com/[email protected]/build/player/lottie.js"></script>
<script>
const anim = lottie.loadAnimation({
container: document.getElementById("a"),
renderer: "svg",
autoplay: false,
loop: false,
animationData: {
v: "5.5.7", fr: 30, ip: 20, op: 80, w: 100, h: 100,
nm: "marker-seek", ddd: 0, assets: [], layers: [],
markers: [{ tm: 30, cm: "middle", dr: 0 }],
},
});
anim.addEventListener("DOMLoaded", () => {
anim.goToAndStop("middle");
// currentRawFrame is 30 and firstFrame is 20, so the renderer draws
// file frame 50. The marker sits at file frame 30.
console.log(anim.currentRawFrame + anim.firstFrame); // 50, expected 30
});
</script>The same off-by-firstFrame appears whenever a segment is active: with ip: 0 and setSegment(20, 50), seeking to the same marker also lands on file frame 50.
The fix is one line in goToAndStop's marker branch:
this.goToAndStop(marker.time - this.firstFrame, true);Measured against 5.13.0: the two broken cases above land on file frame 30, and the ordinary case (ip: 0, no segment, which is almost every animation) is byte-for-byte unaffected, because there firstFrame is 0.
One open edge for review: a marker outside the active range now yields a negative frame (segment 40..60, marker at 30 gives -10). The one-line fix leaves that unclamped, which matches what plain numeric seeks already do; clamping would be a separate behavior change.
Source: airbnb/lottie-web