feat(smooth-scroll): implement smooth scrolling functionality with Lenis integration
This commit is contained in:
@@ -62,6 +62,23 @@ body::before {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
html.lenis,
|
||||
html.lenis body {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.lenis.lenis-smooth {
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
|
||||
.lenis.lenis-smooth [data-lenis-prevent] {
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.lenis.lenis-stopped {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.font-heading {
|
||||
font-family: "Space Grotesk", "Manrope", "Segoe UI", sans-serif;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
import "currency-flags/dist/currency-flags.min.css";
|
||||
|
||||
import { SmoothScrollProvider } from "@/components/providers/smooth-scroll-provider";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "NexCurrency | Modern Currency & Crypto Converter",
|
||||
description:
|
||||
@@ -17,7 +19,7 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en" className="dark" suppressHydrationWarning>
|
||||
<body className="font-sans antialiased">
|
||||
{children}
|
||||
<SmoothScrollProvider>{children}</SmoothScrollProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
111
components/providers/smooth-scroll-provider.tsx
Normal file
111
components/providers/smooth-scroll-provider.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
"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;
|
||||
}
|
||||
@@ -45,6 +45,7 @@ const CommandList = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.List
|
||||
ref={ref}
|
||||
data-lenis-prevent=""
|
||||
className={cn("max-h-[280px] overflow-y-auto overflow-x-hidden", className)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
27
package-lock.json
generated
27
package-lock.json
generated
@@ -16,6 +16,7 @@
|
||||
"cryptocurrency-icons": "^0.18.1",
|
||||
"currency-flags": "github:vivekimsit/currency-flags",
|
||||
"framer-motion": "^12.36.0",
|
||||
"lenis": "^1.3.18",
|
||||
"lucide-react": "^0.475.0",
|
||||
"next": "^14.2.24",
|
||||
"react": "^18.2.0",
|
||||
@@ -4564,6 +4565,32 @@
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/lenis": {
|
||||
"version": "1.3.18",
|
||||
"resolved": "https://registry.npmjs.org/lenis/-/lenis-1.3.18.tgz",
|
||||
"integrity": "sha512-7KBl3V7vx5y1h05pu9fNFZS66I0+1eZ+zUGNNNBKtEn3BONZy+nkHWvdEe2b+zKT+6WX1x7zyOb1zbYYOs6tcg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/darkroomengineering"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nuxt/kit": ">=3.0.0",
|
||||
"react": ">=17.0.0",
|
||||
"vue": ">=3.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@nuxt/kit": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"vue": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/levn": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"cryptocurrency-icons": "^0.18.1",
|
||||
"currency-flags": "github:vivekimsit/currency-flags",
|
||||
"framer-motion": "^12.36.0",
|
||||
"lenis": "^1.3.18",
|
||||
"lucide-react": "^0.475.0",
|
||||
"next": "^14.2.24",
|
||||
"react": "^18.2.0",
|
||||
|
||||
Reference in New Issue
Block a user