72 lines
1.6 KiB
TypeScript
72 lines
1.6 KiB
TypeScript
"use client";
|
|
|
|
import type { ReactNode } from "react";
|
|
import { useEffect, useRef } from "react";
|
|
|
|
export function SmoothScrollProvider({
|
|
children,
|
|
}: {
|
|
children: ReactNode;
|
|
}) {
|
|
const teardownRef = useRef<() => void>(() => {});
|
|
|
|
useEffect(() => {
|
|
const prefersReducedMotion = window.matchMedia(
|
|
"(prefers-reduced-motion: reduce)",
|
|
).matches;
|
|
const hasCoarsePointer = window.matchMedia("(pointer: coarse)").matches;
|
|
|
|
if (prefersReducedMotion || hasCoarsePointer) {
|
|
return undefined;
|
|
}
|
|
|
|
let isMounted = true;
|
|
|
|
const setupSmoothScroll = async () => {
|
|
const [{ default: Lenis }, { gsap }, { ScrollTrigger }] =
|
|
await Promise.all([
|
|
import("lenis"),
|
|
import("gsap"),
|
|
import("gsap/ScrollTrigger"),
|
|
]);
|
|
|
|
if (!isMounted) {
|
|
return;
|
|
}
|
|
|
|
gsap.registerPlugin(ScrollTrigger);
|
|
|
|
const lenis = new Lenis({
|
|
duration: 1.2,
|
|
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
|
|
smoothWheel: true,
|
|
});
|
|
|
|
const handleScroll = () => ScrollTrigger.update();
|
|
const tick = (time: number) => {
|
|
lenis.raf(time * 1000);
|
|
};
|
|
|
|
lenis.on("scroll", handleScroll);
|
|
gsap.ticker.add(tick);
|
|
gsap.ticker.lagSmoothing(0);
|
|
|
|
teardownRef.current = () => {
|
|
lenis.off?.("scroll", handleScroll);
|
|
gsap.ticker.remove(tick);
|
|
lenis.destroy();
|
|
};
|
|
};
|
|
|
|
void setupSmoothScroll();
|
|
|
|
return () => {
|
|
isMounted = false;
|
|
teardownRef.current();
|
|
teardownRef.current = () => {};
|
|
};
|
|
}, []);
|
|
|
|
return <>{children}</>;
|
|
}
|