Files
nexcurrency/components/providers/smooth-scroll-provider.tsx

112 lines
2.5 KiB
TypeScript

"use client";
import { useEffect, type ReactNode } from "react";
import { useReducedMotion } from "framer-motion";
import Lenis from "lenis";
interface SmoothScrollProviderProps {
children: ReactNode;
}
const DESKTOP_POINTER_QUERY = "(hover: hover) and (pointer: fine)";
const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
export function SmoothScrollProvider({ children }: SmoothScrollProviderProps) {
const shouldReduceMotion = useReducedMotion();
useEffect(() => {
if (typeof window === "undefined") {
return;
}
const desktopPointerMedia = window.matchMedia(DESKTOP_POINTER_QUERY);
const reducedMotionMedia = window.matchMedia(REDUCED_MOTION_QUERY);
let lenis: Lenis | null = null;
let frameId = 0;
const stopLenis = () => {
if (frameId) {
window.cancelAnimationFrame(frameId);
frameId = 0;
}
if (lenis) {
lenis.destroy();
lenis = null;
}
};
const startLenis = () => {
if (lenis) {
return;
}
lenis = new Lenis({
lerp: 0.085,
smoothWheel: true,
syncTouch: false,
wheelMultiplier: 0.92,
anchors: true,
prevent: (node) => {
if (!(node instanceof HTMLElement)) {
return false;
}
return Boolean(
node.closest(
"[data-lenis-prevent], [data-radix-scroll-lock], [cmdk-list]",
),
);
},
});
const raf = (time: number) => {
lenis?.raf(time);
frameId = window.requestAnimationFrame(raf);
};
frameId = window.requestAnimationFrame(raf);
};
const syncState = () => {
if (
shouldReduceMotion ||
reducedMotionMedia.matches ||
!desktopPointerMedia.matches
) {
stopLenis();
return;
}
startLenis();
};
syncState();
const handleDesktopPointerChange = () => {
syncState();
};
const handleReducedMotionChange = () => {
syncState();
};
desktopPointerMedia.addEventListener("change", handleDesktopPointerChange);
reducedMotionMedia.addEventListener("change", handleReducedMotionChange);
return () => {
desktopPointerMedia.removeEventListener(
"change",
handleDesktopPointerChange,
);
reducedMotionMedia.removeEventListener(
"change",
handleReducedMotionChange,
);
stopLenis();
};
}, [shouldReduceMotion]);
return children;
}