perf: пауза шейдера на главной при скролле >1000px

This commit is contained in:
root committed 2026-10-05 15:19:29 +02:00
1 parent 8eeeb9eb0a
commit 276c447567
1 file changed
+51 -1
+51 -1
View File
@@ -29,6 +29,26 @@ function paletteFromTheme(root) {
];
}
// Статичный CSS-fallback фона. Переписывает --page-bg напрямую, чтобы
// обновляться при смене акцента даже если WebGL отключён (мобильные).
function setStaticPageBg(root) {
const h = parseFloat(root.style.getPropertyValue("--theme-h")) || 210;
const s = parseFloat(root.style.getPropertyValue("--theme-s")) || 90;
const l = parseFloat(root.style.getPropertyValue("--theme-l")) || 40;
const isDark = root.dataset.theme === "dark";
const mixAndStop = isDark ? [[52, 62], [44, 64], [36, 68]] : [[38, 62], [32, 64], [26, 68]];
const hueShift = isDark ? 180 : 90; // тёмная: accent-square-3/+180, светлая: +90
const hues = [h + hueShift, h + 90, h + 270].map(x => ((x % 360) + 360) % 360);
const bg = `color-mix(in srgb, var(--bg) 100%, transparent)`;
let bgStr = "";
for (let i = 0; i < 3; i++) {
const [mix, stop] = mixAndStop[i];
const color = `hsl(${Math.round(hues[i])} calc(${s}% * 1) calc(${Math.round(l)}% * 1))`;
bgStr += `radial-gradient(calc(48% * 1) calc(56% * 1) at var(--mesh-a-x) var(--mesh-a-y), color-mix(in srgb, ${color}, ${bg}) ${mix}%, transparent ${stop}%)${i < 2 ? "," : ""}`;
}
root.style.setProperty("--page-bg", bgStr);
}
function colorVec4Array(colors) {
// Шейдер ждёт u_colors как vec4[10] → массив из 10 под-массивов [r,g,b,a].
// setUniformValues сам flatten'ит массив массивов в uniform4fv.
@@ -62,6 +82,25 @@ function disposeMount() {
}
}
function updateCanvasVisibility(root) {
const host = root.querySelector(".page-mesh-bg canvas");
if (!host) return;
const shouldHide = root.dataset.meshBg === "off" || document.body.dataset.scrollHidden === "1";
host.style.visibility = shouldHide ? "hidden" : "visible";
}
function handleScroll(root) {
// Шэйдер закрывается, когда прокручено больше 1000px — в дизайне
// mesh-bg перекрывается фоном секций и всё равно не виден.
// Это освобождает GPU на весь остальной скролл.
if (document.body.scrollTop > 1000 || document.documentElement.scrollTop > 1000) {
root.dataset.scrollHidden = "1";
} else {
delete root.dataset.scrollHidden;
}
updateCanvasVisibility(root);
}
function buildMount(host, root) {
const sizing = () => ({
u_fit: ShaderFitOptions[defaultObjectSizing.fit],
@@ -115,11 +154,17 @@ function buildMount(host, root) {
// Обновляем палитру при смене акцента/темы.
onTheme = () => {
const next = paletteFromTheme(root);
if (!next || !mount) return;
if (!next || !mount) {
setStaticPageBg(root);
updateCanvasVisibility(root);
return;
}
mount.setUniforms({
u_colors: colorVec4Array(next),
u_colorsCount: next.length,
});
setStaticPageBg(root);
updateCanvasVisibility(root);
};
window.addEventListener("site-theme-applied", onTheme, { passive: true });
}
@@ -138,6 +183,11 @@ export function initPageMeshShader() {
buildMount(host, document.documentElement);
// Скролл-контроль видимости canvas: при скролле >1000px фон перекрывается
// секциями и не виден — закрываем canvas, чтобы не тратить GPU.
window.addEventListener("scroll", () => handleScroll(document.documentElement), { passive: true });
handleScroll(document.documentElement);
// Чистим ресурсы при нормальном уходе со страницы.
if (!pagehideBound) {
pagehideBound = true;