108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
"use client";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { gsap } from "gsap";
|
|
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
|
|
import Lenis from "lenis";
|
|
import { usePathname } from "next/navigation";
|
|
|
|
const SmoothScroll = () => {
|
|
const lenisRef = useRef<Lenis | null>(null);
|
|
const pathname = usePathname();
|
|
const [isNavigating, setIsNavigating] = useState(false);
|
|
|
|
// Handle pathname changes - PRIORITY 1
|
|
useEffect(() => {
|
|
setIsNavigating(true);
|
|
|
|
// Stop Lenis completely
|
|
if (lenisRef.current) {
|
|
lenisRef.current.stop();
|
|
lenisRef.current.scrollTo(0, { immediate: true, force: true, lock: true });
|
|
}
|
|
|
|
// Force scroll to top with all methods
|
|
window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
|
|
window.scrollTo(0, 0);
|
|
document.documentElement.scrollTop = 0;
|
|
document.body.scrollTop = 0;
|
|
|
|
// Keep forcing scroll for a brief period
|
|
const forceScroll = () => {
|
|
window.scrollTo(0, 0);
|
|
document.documentElement.scrollTop = 0;
|
|
document.body.scrollTop = 0;
|
|
};
|
|
|
|
// Force scroll every 16ms (one frame) for 200ms
|
|
const intervalId = setInterval(forceScroll, 16);
|
|
|
|
// After navigation is settled, restart Lenis
|
|
const restartTimeout = setTimeout(() => {
|
|
clearInterval(intervalId);
|
|
|
|
if (lenisRef.current) {
|
|
lenisRef.current.scrollTo(0, { immediate: true, force: true });
|
|
lenisRef.current.start();
|
|
}
|
|
|
|
setIsNavigating(false);
|
|
|
|
// Final scroll enforcement
|
|
window.scrollTo(0, 0);
|
|
document.documentElement.scrollTop = 0;
|
|
document.body.scrollTop = 0;
|
|
}, 200);
|
|
|
|
return () => {
|
|
clearInterval(intervalId);
|
|
clearTimeout(restartTimeout);
|
|
};
|
|
}, [pathname]);
|
|
|
|
// Initialize Lenis - PRIORITY 2
|
|
useEffect(() => {
|
|
gsap.registerPlugin(ScrollTrigger);
|
|
|
|
const lenis = new Lenis({
|
|
duration: 1.2,
|
|
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
|
|
orientation: 'vertical',
|
|
gestureOrientation: 'vertical',
|
|
smoothWheel: true,
|
|
wheelMultiplier: 1,
|
|
smoothTouch: false,
|
|
touchMultiplier: 2,
|
|
infinite: false,
|
|
});
|
|
|
|
lenisRef.current = lenis;
|
|
|
|
// Force initial scroll to top
|
|
lenis.scrollTo(0, { immediate: true, force: true });
|
|
window.scrollTo(0, 0);
|
|
|
|
// Connect to GSAP ticker
|
|
const tickerCallback = (time: number) => {
|
|
if (!isNavigating) {
|
|
lenis.raf(time * 350);
|
|
}
|
|
};
|
|
|
|
gsap.ticker.add(tickerCallback);
|
|
gsap.ticker.lagSmoothing(0);
|
|
|
|
// Sync with ScrollTrigger
|
|
lenis.on('scroll', ScrollTrigger.update);
|
|
|
|
return () => {
|
|
lenis.destroy();
|
|
gsap.ticker.remove(tickerCallback);
|
|
lenisRef.current = null;
|
|
};
|
|
}, []);
|
|
|
|
return null;
|
|
};
|
|
|
|
export default SmoothScroll;
|