92 lines
3.1 KiB
JavaScript
92 lines
3.1 KiB
JavaScript
import { prefersReducedMotion } from "../shared/media.js";
|
|
|
|
export function initPageMeshBackground() {
|
|
if (prefersReducedMotion()) return;
|
|
|
|
const root = document.documentElement;
|
|
let rafId = 0;
|
|
let showMarkers = false;
|
|
const markers = [];
|
|
|
|
function createMarker(label) {
|
|
const el = document.createElement("div");
|
|
el.textContent = label;
|
|
el.style.position = "fixed";
|
|
el.style.zIndex = "10000";
|
|
el.style.width = "18px";
|
|
el.style.height = "18px";
|
|
el.style.borderRadius = "999px";
|
|
el.style.background = "#000";
|
|
el.style.color = "#fff";
|
|
el.style.fontSize = "11px";
|
|
el.style.fontWeight = "700";
|
|
el.style.lineHeight = "18px";
|
|
el.style.textAlign = "center";
|
|
el.style.pointerEvents = "none";
|
|
el.style.boxShadow = "0 0 0 1px rgba(255,255,255,0.55)";
|
|
document.body.appendChild(el);
|
|
return el;
|
|
}
|
|
|
|
function setMarker(el, xPct, yPct) {
|
|
el.style.left = `calc(${xPct}% - 9px)`;
|
|
el.style.top = `calc(${yPct}% - 9px)`;
|
|
}
|
|
|
|
markers.push(createMarker("A"));
|
|
markers.push(createMarker("B"));
|
|
markers.push(createMarker("C"));
|
|
|
|
const markersToggle = document.querySelector("[data-mesh-markers-toggle]");
|
|
if (markersToggle instanceof HTMLInputElement) {
|
|
showMarkers = markersToggle.checked;
|
|
markersToggle.addEventListener("change", () => {
|
|
showMarkers = markersToggle.checked;
|
|
markers.forEach((m) => {
|
|
m.style.display = showMarkers ? "block" : "none";
|
|
});
|
|
});
|
|
markers.forEach((m) => {
|
|
m.style.display = showMarkers ? "block" : "none";
|
|
});
|
|
}
|
|
|
|
const hero = document.querySelector(".hero");
|
|
const spots = [
|
|
{ x: 2, y: 2 },
|
|
{ x: 50, y: 2 },
|
|
{ x: 98, y: 2 },
|
|
];
|
|
|
|
const update = () => {
|
|
rafId = 0;
|
|
const heroHeight = hero instanceof HTMLElement ? Math.max(hero.offsetHeight, 1) : Math.max(window.innerHeight, 1);
|
|
const heroProgress = Math.min(window.scrollY / heroHeight, 1);
|
|
|
|
const opacityMaxRaw = getComputedStyle(root).getPropertyValue("--mesh-opacity-max").trim();
|
|
const opacityMinRaw = getComputedStyle(root).getPropertyValue("--mesh-opacity-min").trim();
|
|
const opacityMax = Number.parseFloat(opacityMaxRaw);
|
|
const opacityMin = Number.parseFloat(opacityMinRaw);
|
|
const safeOpacityMax = Number.isFinite(opacityMax) ? opacityMax : 1;
|
|
const safeOpacityMin = Number.isFinite(opacityMin) ? opacityMin : 0;
|
|
const opacityNow = safeOpacityMax - (safeOpacityMax - safeOpacityMin) * heroProgress;
|
|
root.style.setProperty("--mesh-opacity-current", `${Math.max(opacityNow, safeOpacityMin).toFixed(3)}`);
|
|
|
|
spots.forEach((p, idx) => {
|
|
const key = ["a", "b", "c"][idx];
|
|
root.style.setProperty(`--mesh-${key}-x`, `${p.x.toFixed(2)}%`);
|
|
root.style.setProperty(`--mesh-${key}-y`, `${p.y.toFixed(2)}%`);
|
|
if (showMarkers) setMarker(markers[idx], p.x, p.y);
|
|
});
|
|
};
|
|
|
|
const onScroll = () => {
|
|
if (rafId) return;
|
|
rafId = window.requestAnimationFrame(update);
|
|
};
|
|
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
window.addEventListener("resize", onScroll, { passive: true });
|
|
update();
|
|
}
|