Unify scroll reveals, hero entrance, nav active-underline, button springs, the scroll-progress bar and backdrop parallax under one Motion system (src/scripts/anim.ts), bundled into a self-hosted /_astro module so CSP script-src 'self' holds. Progress bar + parallax now scroll()-driven and cross-browser (was Chromium-only animation-timeline). Fail-open, reduced-motion aware, content fully visible with JS off.
This commit is contained in:
+9
-54
@@ -12,6 +12,11 @@
|
||||
document.documentElement.dataset.theme = "dark";
|
||||
}
|
||||
|
||||
// Mark JS as live BEFORE first paint. CSS only hides the pre-reveal animation
|
||||
// state under `html.js`, so with JS off every element stays visible (the
|
||||
// Motion layer in /_astro reveals them once it runs). Progressive enhancement.
|
||||
document.documentElement.classList.add("js");
|
||||
|
||||
function onReady(fn) {
|
||||
if (document.readyState !== "loading") fn();
|
||||
else document.addEventListener("DOMContentLoaded", fn);
|
||||
@@ -33,59 +38,9 @@
|
||||
});
|
||||
}
|
||||
|
||||
// --- Reveal on scroll (progressive enhancement, motion-aware) ----------
|
||||
var reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
var targets = document.querySelectorAll("[data-reveal]");
|
||||
if (!reduce && "IntersectionObserver" in window) {
|
||||
targets.forEach(function (el) {
|
||||
el.classList.add("reveal");
|
||||
});
|
||||
var io = new IntersectionObserver(
|
||||
function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add("is-visible");
|
||||
io.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ rootMargin: "0px 0px -10% 0px", threshold: 0.1 },
|
||||
);
|
||||
targets.forEach(function (el) {
|
||||
io.observe(el);
|
||||
});
|
||||
}
|
||||
|
||||
// --- Scroll-coupled background pan -------------------------------------
|
||||
// The Bat-Computer backdrop pans vertically in lock-step with scroll —
|
||||
// moving ONLY while you scroll, still at rest. Plain scroll listener so it
|
||||
// works in every browser (unlike CSS animation-timeline: scroll(), which is
|
||||
// Chromium-only). rAF-throttled; sets a CSS var the transform reads.
|
||||
var bgImgs = document.querySelectorAll(".site-bg__img");
|
||||
if (bgImgs.length) {
|
||||
var ticking = false;
|
||||
function updateBg() {
|
||||
ticking = false;
|
||||
var doc = document.documentElement;
|
||||
var max = doc.scrollHeight - window.innerHeight;
|
||||
var p = max > 0 ? Math.min(1, Math.max(0, window.scrollY / max)) : 0;
|
||||
// +half a range at the top → -half at the bottom: scrolling down pans
|
||||
// the camera down the board. Range kept within the image's overhang so
|
||||
// an edge never shows (image is 215% tall → 57.5vh spare each side).
|
||||
var shift = (0.5 - p) * window.innerHeight * 0.7;
|
||||
for (var i = 0; i < bgImgs.length; i++) {
|
||||
bgImgs[i].style.setProperty("--bg-shift", shift.toFixed(1) + "px");
|
||||
}
|
||||
}
|
||||
function onScroll() {
|
||||
if (!ticking) {
|
||||
ticking = true;
|
||||
window.requestAnimationFrame(updateBg);
|
||||
}
|
||||
}
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
window.addEventListener("resize", onScroll, { passive: true });
|
||||
updateBg();
|
||||
}
|
||||
// Scroll reveals, hero entrance, backdrop parallax, scroll-progress bar,
|
||||
// nav underline and button springs all live in the Motion layer
|
||||
// (src/scripts/anim.ts → bundled /_astro/*.js). This file owns only the
|
||||
// pre-paint theme so it can stay a tiny blocking <head> script.
|
||||
});
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user