Adds consultation leads, section editors, and the one-time content reset for this release. Co-authored-by: Cursor <cursoragent@cursor.com>
78 lines
2.1 KiB
TypeScript
78 lines
2.1 KiB
TypeScript
import Lenis from "lenis";
|
|
|
|
const REVEAL_SELECTOR = ".fade-item:not(.revealed)";
|
|
const REVEAL_SCROLL_RATIO = 0.88;
|
|
const REVEAL_INITIAL_RATIO = 0.92;
|
|
const INITIAL_REVEAL_DELAY_MS = 150;
|
|
|
|
/**
|
|
* Inertial scrolling plus the two scroll-driven effects that depend on it:
|
|
* the top progress bar and the `.fade-item` reveal.
|
|
*/
|
|
export default defineNuxtPlugin((nuxtApp) => {
|
|
const lenis = new Lenis({
|
|
duration: 1.2,
|
|
easing: (t: number) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
|
|
orientation: "vertical",
|
|
smoothWheel: true,
|
|
wheelMultiplier: 1.05,
|
|
touchMultiplier: 1.8,
|
|
});
|
|
|
|
let pending: HTMLElement[] = [];
|
|
let progressEl: HTMLElement | null = null;
|
|
|
|
const collect = () => {
|
|
pending = Array.from(document.querySelectorAll<HTMLElement>(REVEAL_SELECTOR));
|
|
progressEl = document.querySelector<HTMLElement>(".scroll-progress");
|
|
};
|
|
|
|
const reveal = (ratio: number) => {
|
|
if (!pending.length) return;
|
|
const limit = window.innerHeight * ratio;
|
|
const hits: HTMLElement[] = [];
|
|
pending = pending.filter((el) => {
|
|
if (!el.isConnected) return false;
|
|
if (el.getBoundingClientRect().top <= limit) {
|
|
hits.push(el);
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
for (const el of hits) el.classList.add("revealed");
|
|
};
|
|
|
|
lenis.on("scroll", (e: Lenis) => {
|
|
if (progressEl) {
|
|
const total = document.documentElement.scrollHeight - window.innerHeight;
|
|
const progress = total > 0 ? e.scroll / total : 0;
|
|
progressEl.style.transform = `scaleX(${progress})`;
|
|
}
|
|
reveal(REVEAL_SCROLL_RATIO);
|
|
});
|
|
|
|
let rafId = 0;
|
|
const loop = (time: number) => {
|
|
lenis.raf(time);
|
|
rafId = requestAnimationFrame(loop);
|
|
};
|
|
rafId = requestAnimationFrame(loop);
|
|
|
|
const initialReveal = () => {
|
|
collect();
|
|
window.setTimeout(() => {
|
|
collect();
|
|
reveal(REVEAL_INITIAL_RATIO);
|
|
}, INITIAL_REVEAL_DELAY_MS);
|
|
};
|
|
|
|
nuxtApp.hook("app:mounted", initialReveal);
|
|
nuxtApp.hook("page:finish", initialReveal);
|
|
nuxtApp.vueApp.onUnmount(() => {
|
|
cancelAnimationFrame(rafId);
|
|
lenis.destroy();
|
|
});
|
|
|
|
return { provide: { lenis } };
|
|
});
|