fix(studio): keep gsap timeline clips stable

This commit is contained in:
Miguel Ángel
2026-04-28 17:20:24 -04:00
parent f142a63e63
commit 78d949b844
2 changed files with 89 additions and 3 deletions
@@ -402,6 +402,92 @@ describe("collectRuntimeTimelinePayload", () => {
expect(s2?.duration).toBe(3);
});
it("does not offset GSAP scene clips by the master timeline start time", () => {
const root = document.createElement("div");
root.setAttribute("data-composition-id", "main");
root.setAttribute("data-duration", "20");
document.body.appendChild(root);
const scene = document.createElement("div");
scene.id = "scene-1";
root.appendChild(scene);
const masterTimeline = {
duration: () => 20,
time: () => 1.25,
play: () => {},
pause: () => {},
seek: () => {},
add: () => {},
paused: () => {},
set: () => {},
startTime: () => 1.25,
getChildren: () => [
{
targets: () => [scene],
startTime: () => 4,
duration: () => 3,
parent: masterTimeline,
},
],
};
(window as any).__timelines = {
main: masterTimeline,
};
const result = collectRuntimeTimelinePayload(defaultParams);
const clip = result.clips.find((c) => c.id === "scene-1");
expect(clip).toBeDefined();
expect(clip?.start).toBe(4);
expect(clip?.duration).toBe(3);
});
it("keeps nested GSAP timeline offsets below the master timeline", () => {
const root = document.createElement("div");
root.setAttribute("data-composition-id", "main");
root.setAttribute("data-duration", "20");
document.body.appendChild(root);
const scene = document.createElement("div");
scene.id = "scene-1";
root.appendChild(scene);
const masterTimeline = {
duration: () => 20,
time: () => 1.25,
play: () => {},
pause: () => {},
seek: () => {},
add: () => {},
paused: () => {},
set: () => {},
startTime: () => 1.25,
getChildren: () => [] as unknown[],
};
const nestedTimeline = {
startTime: () => 6,
parent: masterTimeline,
};
const nestedTween = {
targets: () => [scene],
startTime: () => 2,
duration: () => 3,
parent: nestedTimeline,
};
masterTimeline.getChildren = () => [nestedTween];
(window as any).__timelines = {
main: masterTimeline,
};
const result = collectRuntimeTimelinePayload(defaultParams);
const clip = result.clips.find((c) => c.id === "scene-1");
expect(clip).toBeDefined();
expect(clip?.start).toBe(8);
expect(clip?.duration).toBe(3);
});
it("bubbles child tween ranges up to scene-level ancestors", () => {
const root = document.createElement("div");
root.setAttribute("data-composition-id", "main");
+3 -3
View File
@@ -416,7 +416,7 @@ export function collectRuntimeTimelinePayload(params: {
targets?: () => Element[];
startTime?: () => number;
duration?: () => number;
parent?: { startTime?: () => number };
parent?: GsapTween;
};
const tlWithChildren = masterTimeline as typeof masterTimeline & {
getChildren?: (nested: boolean, tweens: boolean, timelines: boolean) => GsapTween[];
@@ -452,9 +452,9 @@ export function collectRuntimeTimelinePayload(params: {
continue;
let tweenStart = tween.startTime();
let parent = tween.parent;
while (parent && typeof parent.startTime === "function") {
while (parent && parent !== masterTimeline && typeof parent.startTime === "function") {
tweenStart += parent.startTime();
parent = (parent as GsapTween).parent;
parent = parent.parent;
}
const tweenEnd = tweenStart + tween.duration();
if (!Number.isFinite(tweenStart) || !Number.isFinite(tweenEnd)) continue;