agency-web/app/components/SmoothScroll.tsx

55 lines
1.5 KiB
TypeScript

"use client";
import { useEffect } from "react";
import Lenis from "lenis";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
/**
* Lenis smooth scroll wired into GSAP's ScrollTrigger so scroll-driven
* animation stays in sync. Disabled entirely under reduced-motion.
*/
export default function SmoothScroll() {
useEffect(() => {
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduce) return;
gsap.registerPlugin(ScrollTrigger);
const lenis = new Lenis({
duration: 1.1,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
smoothWheel: true,
});
lenis.on("scroll", ScrollTrigger.update);
const raf = (time: number) => lenis.raf(time * 1000);
gsap.ticker.add(raf);
gsap.ticker.lagSmoothing(0);
// anchor links route through Lenis
const onClick = (e: MouseEvent) => {
const a = (e.target as HTMLElement)?.closest('a[href^="#"]') as
| HTMLAnchorElement
| null;
if (!a) return;
const id = a.getAttribute("href");
if (!id || id === "#") return;
const el = document.querySelector(id);
if (!el) return;
e.preventDefault();
lenis.scrollTo(el as HTMLElement, { offset: -80 });
};
document.addEventListener("click", onClick);
return () => {
document.removeEventListener("click", onClick);
gsap.ticker.remove(raf);
lenis.destroy();
ScrollTrigger.getAll().forEach((t) => t.kill());
};
}, []);
return null;
}