mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
fix(shader-transitions): page-side render dropped shader transitions and final-scene content
Page-side compositing (default on) silently dropped HyperShader.init shader transitions in the engine render. The compositor clones the from/to scenes to feed drawElementImage, but cloneNode copies the GSAP opacity-fade, and Chrome won't paint hidden elements, so drawElementImage throws "No cached paint record" and the shader degrades to a hard cut. Force the clones visible before capture, as the html2canvas path already does via forceSceneVisibleInClone. Also fixes the final scene's content dropping in the last beat: the core clip runtime hides it shortly before the composition ends, and page-side screenshots the live page (the layered path survives via forceVisible per-scene capture). Un-hide the settled scene on non-transition frames. Page-side only; retains the ~6.5x page-side speedup.
This commit is contained in:
@@ -196,6 +196,16 @@ export function installPageSideCompositor(options: PageCompositorInstallOptions)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Scene on screen at a non-transition time: after the last transition whose
|
||||
// window has passed. Full transitions list so the index matches scene order.
|
||||
function settledSceneIdAt(time: number): string | undefined {
|
||||
let idx = 0;
|
||||
for (const t of transitions) {
|
||||
if (time >= t.time + (t.duration ?? defaultDuration)) idx += 1;
|
||||
}
|
||||
return scenes[Math.min(idx, scenes.length - 1)];
|
||||
}
|
||||
|
||||
let currentActive: ResolvedTransition | null = null;
|
||||
let currentProgress = 0;
|
||||
|
||||
@@ -224,8 +234,24 @@ export function installPageSideCompositor(options: PageCompositorInstallOptions)
|
||||
}
|
||||
while (fromStaging.firstChild) fromStaging.removeChild(fromStaging.firstChild);
|
||||
while (toStaging.firstChild) toStaging.removeChild(toStaging.firstChild);
|
||||
fromStaging.appendChild(fromEl.cloneNode(true));
|
||||
toStaging.appendChild(toEl.cloneNode(true));
|
||||
const fromClone = fromEl.cloneNode(true) as HTMLElement;
|
||||
const toClone = toEl.cloneNode(true) as HTMLElement;
|
||||
fromStaging.appendChild(fromClone);
|
||||
toStaging.appendChild(toClone);
|
||||
|
||||
// cloneNode copies the GSAP opacity-fade (opacity:0 / hidden data-start), and
|
||||
// Chrome won't paint hidden elements — drawElementImage then throws "No cached
|
||||
// paint record" and the shader degrades to a hard cut. The shader blends from
|
||||
// full-opacity textures via u_progress, so force the clones visible. Cf.
|
||||
// forceSceneVisibleInClone (html2canvas path).
|
||||
for (const clone of [fromClone, toClone]) {
|
||||
clone.style.opacity = "1";
|
||||
clone.style.visibility = "visible";
|
||||
clone.querySelectorAll<HTMLElement>("[data-start]").forEach((el) => {
|
||||
el.style.opacity = "1";
|
||||
el.style.visibility = "visible";
|
||||
});
|
||||
}
|
||||
|
||||
// Decode any data-URI images in clones so the browser has current
|
||||
// bitmaps before the micro-screenshot forces a paint pass.
|
||||
@@ -326,6 +352,14 @@ export function installPageSideCompositor(options: PageCompositorInstallOptions)
|
||||
pWin.__hf_page_composite_pending = false;
|
||||
while (fromStaging.firstChild) fromStaging.removeChild(fromStaging.firstChild);
|
||||
while (toStaging.firstChild) toStaging.removeChild(toStaging.firstChild);
|
||||
// Live-page screenshot parity with the layered path's forceVisible: the
|
||||
// core clip runtime hides the final scene a beat before the comp ends, so
|
||||
// un-hide the settled scene (others stay at opacity 0).
|
||||
const settledId = settledSceneIdAt(time);
|
||||
const settled = settledId ? document.getElementById(settledId) : null;
|
||||
if (settled instanceof HTMLElement && settled.style.visibility === "hidden") {
|
||||
settled.style.visibility = "visible";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
currentActive = active;
|
||||
|
||||
Reference in New Issue
Block a user