From 4c6e26cb4e8429baa2995af95810f7f26a114861 Mon Sep 17 00:00:00 2001 From: bender Date: Tue, 9 Jun 2026 23:37:50 +0000 Subject: [PATCH] Add src/lib/animations/lenis-smooth-scroll.tsx --- src/lib/animations/lenis-smooth-scroll.tsx | 79 ++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/lib/animations/lenis-smooth-scroll.tsx diff --git a/src/lib/animations/lenis-smooth-scroll.tsx b/src/lib/animations/lenis-smooth-scroll.tsx new file mode 100644 index 0000000..e5aa123 --- /dev/null +++ b/src/lib/animations/lenis-smooth-scroll.tsx @@ -0,0 +1,79 @@ +"use client"; + +import { ReactNode, useEffect } from "react"; +import Lenis from "@studio-freight/lenis"; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; + +interface LenisProviderProps { + children: ReactNode; + root?: boolean; // If true, applies to the window scroll. If false, applies to a specific div. + className?: string; // Only relevant if root is false +} + +/** + * Provides smooth scrolling using Lenis. + * This component should ideally wrap the entire application in layout.tsx. + * If `root` is false, it can be applied to a specific scrollable div. + */ +export function LenisSmoothScroll({ children, root = true, className }: LenisProviderProps) { + useEffect(() => { + const lenis = new Lenis({ + duration: 1.2, + easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), // https://www.desmos.com/calculator/brs54l4xgi + orientation: "vertical", gestureOrientation: "vertical", smoothWheel: true, + wheelMultiplier: 1, + smoothTouch: false, + touchMultiplier: 2, + syncTouch: false, + infinite: false, + }); + + function raf(time: DOMHighResTimeStamp) { + lenis.raf(time); + requestAnimationFrame(raf); + } + + requestAnimationFrame(raf); + + // Integrate with GSAP ScrollTrigger + // This is crucial for GSAP ScrollTrigger to work correctly with Lenis. + // Ensure GSAP and ScrollTrigger are already registered globally or imported locally. + const syncScroll = () => { + // Check if GSAP and ScrollTrigger are available globally or imported. + // If using GSAP context, pass the context. + if (typeof window !== 'undefined' && window.innerWidth > 768) { // Example: only enable for larger screens + lenis.on('scroll', ScrollTrigger.update); + gsap.ticker.add((time) => { + lenis.raf(time * 1000) + }) + gsap.ticker.lagSmoothing(0) + } + }; + + // Delay syncScroll to ensure ScrollTrigger is registered if it's external + const timeout = setTimeout(syncScroll, 0); + + + return () => { + clearTimeout(timeout); + lenis.destroy(); + // Remove the listener for GSAP ScrollTrigger if it was added + if (typeof window !== 'undefined' && window.innerWidth > 768) { + gsap.ticker.remove(lenis.raf) + // lenis.off('scroll', ScrollTrigger.update); // Lenis handles removing its own listeners + } + }; + }, []); + + if (root) { + return <>{children}; + } else { + // If not root, apply to a specific div + return ( +
+ {children} +
+ ); + } +}