"use client"; /** * Lenis smooth scroll wired into GSAP ScrollTrigger. * - Lenis drives the rAF loop; ScrollTrigger.update() runs on every Lenis tick * so pinned/scrubbed animations stay in lockstep with the smoothed scroll. * - Disabled entirely when prefers-reduced-motion is set (native scroll). * - Exposes a scroll-progress value on :root for the top progress bar (CSS). */ import { useEffect } from "react"; import Lenis from "lenis"; import { gsap, ScrollTrigger } from "./gsap"; export default function SmoothScroll() { useEffect(() => { const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches; // Always keep the CSS progress var fed (cheap), even without Lenis. const setProgress = (p: number) => document.documentElement.style.setProperty("--scroll-progress", String(p)); if (reduce) { const onScroll = () => { const h = document.documentElement; const max = h.scrollHeight - h.clientHeight; setProgress(max > 0 ? h.scrollTop / max : 0); }; window.addEventListener("scroll", onScroll, { passive: true }); onScroll(); return () => window.removeEventListener("scroll", onScroll); } const lenis = new Lenis({ duration: 1.1, easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), smoothWheel: true, }); lenis.on("scroll", (e: { progress: number }) => { ScrollTrigger.update(); setProgress(e.progress); }); const raf = (time: number) => lenis.raf(time * 1000); gsap.ticker.add(raf); gsap.ticker.lagSmoothing(0); return () => { gsap.ticker.remove(raf); lenis.destroy(); }; }, []); return null; }