mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(core): keep rebound root timelines clock-driven (#5165)
* fix(core): keep rebound root timelines clock-driven * test(core): verify transport advances the rebound root
This commit is contained in:
@@ -3669,6 +3669,61 @@ describe("initSandboxRuntimeModular", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps a root rebound during playback paused on GSAP's ticker", () => {
|
||||
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "Date"] });
|
||||
let nowMs = 1000;
|
||||
vi.spyOn(performance, "now").mockImplementation(() => nowMs);
|
||||
const raf = createManualRaf();
|
||||
window.requestAnimationFrame = raf.requestAnimationFrame;
|
||||
window.cancelAnimationFrame = raf.cancelAnimationFrame;
|
||||
document.body.innerHTML =
|
||||
'<div data-composition-id="main" data-root="true" data-start="0" data-duration="1">' +
|
||||
'<video data-start="0"></video></div>';
|
||||
const video = document.querySelector("video")!;
|
||||
vi.spyOn(video, "play").mockResolvedValue(undefined);
|
||||
vi.spyOn(video, "pause").mockImplementation(() => {});
|
||||
const authored = gsap.timeline({ paused: true });
|
||||
window.gsap = gsap;
|
||||
window.__timelines = { main: authored };
|
||||
const timelines = vi.spyOn(gsap, "timeline");
|
||||
const messages = vi.spyOn(window.parent, "postMessage");
|
||||
|
||||
try {
|
||||
initSandboxRuntimeModular();
|
||||
vi.advanceTimersByTime(0);
|
||||
window.__player!.play();
|
||||
nowMs = 1250;
|
||||
raf.step(250);
|
||||
Object.defineProperty(video, "duration", { value: 12, configurable: true });
|
||||
video.dispatchEvent(new Event("loadedmetadata"));
|
||||
vi.advanceTimersByTime(100);
|
||||
|
||||
expect(
|
||||
messages.mock.calls.some(
|
||||
([message]) =>
|
||||
(message as { code?: string }).code === "timeline_rebind_after_media_metadata",
|
||||
),
|
||||
).toBe(true);
|
||||
const rebound = timelines.mock.results.at(-1)?.value as gsap.core.Timeline;
|
||||
expect(authored.parent).toBe(rebound);
|
||||
expect(window.__player!.isPlaying()).toBe(true);
|
||||
expect(rebound.paused()).toBe(true);
|
||||
const time = rebound.time();
|
||||
gsap.updateRoot(gsap.globalTimeline.time() + 0.5);
|
||||
expect(rebound.time()).toBe(time);
|
||||
expect(window.__player!.getTime()).toBe(0.25);
|
||||
nowMs = 1350;
|
||||
raf.step(100);
|
||||
expect(rebound.time()).toBeCloseTo(0.35, 5);
|
||||
} finally {
|
||||
window.__hfRuntimeTeardown?.();
|
||||
for (const result of timelines.mock.results) {
|
||||
if (result.type === "return") (result.value as gsap.core.Timeline).kill();
|
||||
}
|
||||
authored.kill();
|
||||
}
|
||||
});
|
||||
|
||||
it("sets __renderReady only after timeline is bound, not at __playerReady time", async () => {
|
||||
const root = document.createElement("div");
|
||||
root.setAttribute("data-composition-id", "main");
|
||||
|
||||
@@ -2294,21 +2294,17 @@ export function initSandboxRuntimeModular(): void {
|
||||
return false;
|
||||
}
|
||||
const previousTime = Math.max(0, state.currentTime || 0);
|
||||
const wasPlaying = state.isPlaying;
|
||||
state.capturedTimeline = resolution.timeline;
|
||||
if (typeof state.capturedTimeline.timeScale === "function") {
|
||||
state.capturedTimeline.timeScale(state.playbackRate);
|
||||
}
|
||||
try {
|
||||
// pause guarded separately: a PARTIAL timeline without pause() must not
|
||||
// abort the seek/play restore below (the catch would swallow them too).
|
||||
// abort restoring the transport time below (the catch would swallow it too).
|
||||
pauseTimelineIfPossible(state.capturedTimeline);
|
||||
if (typeof state.capturedTimeline.seek === "function") {
|
||||
state.capturedTimeline.seek(previousTime, false);
|
||||
}
|
||||
if (wasPlaying && typeof state.capturedTimeline.play === "function") {
|
||||
state.capturedTimeline.play();
|
||||
}
|
||||
} catch (err) {
|
||||
// keep runtime resilient even if a timeline implementation throws
|
||||
swallow("runtime.init.site7", err);
|
||||
|
||||
Reference in New Issue
Block a user