fix(mesh-shader): re-init shader after bfcache restore (back/forward nav)
This commit is contained in:
1 parent
adf0f4b052
commit
8a63190f5a
1 file changed
+57
-19
@@ -40,11 +40,29 @@ function colorVec4Array(colors) {
|
||||
return out;
|
||||
}
|
||||
|
||||
export function initPageMeshShader() {
|
||||
const host = document.querySelector(".page-mesh-bg");
|
||||
if (!host) return markMeshReady(); // нет хоста — hero всё равно проявляем
|
||||
if (prefersReducedMotion()) return markMeshReady(); // статичный CSS-fallback, hero сразу виден
|
||||
// Сигнал готовности фона: проявляем hero (см. .hero в main.css).
|
||||
// Ставится во всех ветках инициализации, чтобы hero никогда не висел скрытым.
|
||||
function markMeshReady() {
|
||||
document.documentElement.classList.add("mesh-bg-ready");
|
||||
}
|
||||
|
||||
let mount = null;
|
||||
let onTheme = null;
|
||||
let pagehideBound = false;
|
||||
let pageshowBound = false;
|
||||
|
||||
function disposeMount() {
|
||||
if (onTheme) {
|
||||
window.removeEventListener("site-theme-applied", onTheme);
|
||||
onTheme = null;
|
||||
}
|
||||
if (mount) {
|
||||
try { mount.dispose(); } catch { /* noop */ }
|
||||
mount = null;
|
||||
}
|
||||
}
|
||||
|
||||
function buildMount(host, root) {
|
||||
const sizing = () => ({
|
||||
u_fit: ShaderFitOptions[defaultObjectSizing.fit],
|
||||
u_scale: defaultObjectSizing.scale,
|
||||
@@ -57,7 +75,6 @@ export function initPageMeshShader() {
|
||||
u_worldHeight: defaultObjectSizing.worldHeight,
|
||||
});
|
||||
|
||||
const root = document.documentElement;
|
||||
const base = paletteFromTheme(root) || ["#241d9a", "#f75092", "#9f50d3", "#0e6b8f"];
|
||||
|
||||
const uniforms = {
|
||||
@@ -70,7 +87,6 @@ export function initPageMeshShader() {
|
||||
u_grainOverlay: 0.04,
|
||||
};
|
||||
|
||||
let mount;
|
||||
try {
|
||||
mount = new ShaderMount(
|
||||
host,
|
||||
@@ -83,7 +99,7 @@ export function initPageMeshShader() {
|
||||
2_500_000 // maxPixelCount: потолок ~1600x1000, фон размытый — качество не страдает
|
||||
);
|
||||
// не даём IntersectionObserver ставить паузу вне вьюпорта — фон на весь экран
|
||||
try { mount.intersectionObserver?.disconnect(); } catch {}
|
||||
try { mount.intersectionObserver?.disconnect(); } catch { /* noop */ }
|
||||
|
||||
// Первый кадр уже отрисован в конструкторе — отмечаем готовность,
|
||||
// чтобы CSS плавно проявил canvas (fade-in) и hero.
|
||||
@@ -92,29 +108,51 @@ export function initPageMeshShader() {
|
||||
} catch (e) {
|
||||
console.warn("Mesh shader mount failed, fallback to static bg", e);
|
||||
markMeshReady();
|
||||
mount = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Обновляем палитру при смене акцента/темы.
|
||||
const onTheme = () => {
|
||||
onTheme = () => {
|
||||
const next = paletteFromTheme(root);
|
||||
if (!next) return;
|
||||
if (!next || !mount) return;
|
||||
mount.setUniforms({
|
||||
u_colors: colorVec4Array(next),
|
||||
u_colorsCount: next.length,
|
||||
});
|
||||
};
|
||||
window.addEventListener("site-theme-applied", onTheme, { passive: true });
|
||||
|
||||
// при уходе со страницы — чистим
|
||||
window.addEventListener("pagehide", () => {
|
||||
window.removeEventListener("site-theme-applied", onTheme);
|
||||
try { mount.dispose(); } catch {}
|
||||
}, { once: true });
|
||||
}
|
||||
|
||||
// Сигнал готовности фона: проявляем hero (см. .hero в main.css).
|
||||
// Ставится во всех ветках инициализации, чтобы hero никогда не висел скрытым.
|
||||
function markMeshReady() {
|
||||
document.documentElement.classList.add("mesh-bg-ready");
|
||||
export function initPageMeshShader() {
|
||||
const host = document.querySelector(".page-mesh-bg");
|
||||
if (!host) return markMeshReady(); // нет хоста — hero всё равно проявляем
|
||||
if (prefersReducedMotion()) return markMeshReady(); // статичный CSS-fallback, hero сразу виден
|
||||
|
||||
// Повторный вызов (или восстановление из bfcache): убираем старый canvas/ mount,
|
||||
// чтобы не накапливать мёртвые WebGL-контексты.
|
||||
disposeMount();
|
||||
host.classList.remove("is-ready");
|
||||
const stale = host.querySelector("canvas");
|
||||
if (stale) stale.remove();
|
||||
|
||||
buildMount(host, document.documentElement);
|
||||
|
||||
// Чистим ресурсы при нормальном уходе со страницы.
|
||||
if (!pagehideBound) {
|
||||
pagehideBound = true;
|
||||
window.addEventListener("pagehide", () => {
|
||||
disposeMount();
|
||||
});
|
||||
}
|
||||
|
||||
// Браузерная Back/Forward Cache (bfcache): при возврате на страницу модуль
|
||||
// main.js НЕ перезапускается, а canvas был удалён в pagehide → фон пустой.
|
||||
// pageshow с persisted=true = восстановление из bfcache → пересобираем шэйдер.
|
||||
if (!pageshowBound) {
|
||||
pageshowBound = true;
|
||||
window.addEventListener("pageshow", (e) => {
|
||||
if (e.persisted) initPageMeshShader();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user