perf(core): a video edit with no animation timeline stops rewriting every clip each frame

This commit is contained in:
Miguel Angel Simon Sierra
2026-10-30 00:28:55 -04:00
parent 6e403a9b29
commit 2a44408fcc
2 changed files with 33 additions and 8 deletions
+19
View File
@@ -612,6 +612,25 @@ describe("initSandboxRuntimeModular", () => {
}
});
it("leaves hidden clips alone on every jump when the page has no timeline", () => {
document.body.innerHTML =
`<div data-composition-id="main" data-root="true" data-start="0" data-duration="10" data-width="1920" data-height="1080">` +
`<video id="first" src="a.mp4" data-start="0" data-duration="2" data-track-index="0"></video>` +
`<video id="second" src="a.mp4" data-start="2" data-duration="8" data-track-index="0"></video>` +
`</div>`;
window.__timelines = {};
initSandboxRuntimeModular();
const query = vi.spyOn(document, "querySelectorAll");
const looked = () =>
query.mock.calls.filter(([selector]) => selector === "[data-hf-clip-hidden]").length;
try {
for (const t of [0.5, 1, 1.5, 3, 6]) window.__player?.seek(t);
expect(looked()).toBe(0);
} finally {
query.mockRestore();
}
});
it("styles a hidden clip visible once while playing toward its start, far scene included", () => {
document.body.innerHTML =
`<div data-composition-id="main" data-root="true" data-start="0" data-duration="10" data-width="1920" data-height="1080">` +
+14 -8
View File
@@ -5163,6 +5163,16 @@ export function initSandboxRuntimeModular(): void {
return false;
}
};
// Registered timelines other than the root composition's: each scene's own clock.
const sceneTimelines = () => {
const timelines = (window.__timelines ?? {}) as Record<string, RuntimeTimelineLike | undefined>;
const rootCompositionId =
resolveRootCompositionElement()?.getAttribute("data-composition-id") ?? null;
return Object.entries(timelines).filter(
(entry): entry is [string, RuntimeTimelineLike] =>
!!entry[1] && typeof entry[1] === "object" && entry[0] !== rootCompositionId,
);
};
const seekStandaloneRegisteredTimelines = (
timeSeconds: number,
revision: number,
@@ -5172,12 +5182,7 @@ export function initSandboxRuntimeModular(): void {
sceneClocks = new WeakMap();
sceneClocksRevision = revision;
}
const timelines = (window.__timelines ?? {}) as Record<string, RuntimeTimelineLike | undefined>;
const rootCompositionId =
resolveRootCompositionElement()?.getAttribute("data-composition-id") ?? null;
for (const [compositionId, timeline] of Object.entries(timelines)) {
if (!timeline || typeof timeline !== "object" || compositionId === rootCompositionId)
continue;
for (const [compositionId, timeline] of sceneTimelines()) {
const scene = sceneClockOf(compositionId, timeline);
if (!scene) continue;
const sourceTime =
@@ -5291,9 +5296,10 @@ export function initSandboxRuntimeModular(): void {
// Only clips holding a tween that renders for the first time on this seek need their authored style read.
const firstRendered =
firstRenders && tl ? clipsFirstRenderedIn(tl, tweensRenderedThrough.time, t) : null;
// An empty set means no tween draws for the first time: nothing to show, so no page-wide query.
// An empty set means no tween draws for the first time, and a page with no timeline has no tween:
// nothing to show, so no page-wide query.
const hideClipsAgain =
firstRenders && firstRendered?.size !== 0
firstRenders && firstRendered?.size !== 0 && (tl || sceneTimelines().length > 0)
? showHiddenClips(firstRendered ? (el) => firstRendered.has(el) : undefined)
: null;
// Scenes first, each paused on its own clock, so the root's pass skips them and its later