import type { JSX } from "solid-js"; import { createSignal, onCleanup } from "solid-js"; type SmoothScrollProps = JSX.IntrinsicElements["div"] & { children: JSX.Element; }; export function SmoothScroll(props: SmoothScrollProps) { const [isScrolling, setIsScrolling] = createSignal(false); let animationFrameId: number | null = null; const easeOutQuad = (t: number, b: number, c: number, d: number) => { const time = t / d; return -c * time * (time - 2) + b; }; const smoothScroll = (deltaY: number) => { const scrollSpeed = 3; const currentScroll = window.scrollY; const targetScroll = deltaY * scrollSpeed; const duration = 300; const startTime = performance.now(); const animateScroll = (currentTime: number) => { const elapsedTime = currentTime - startTime; const ease = easeOutQuad( elapsedTime, currentScroll, targetScroll, duration, ); window.scrollTo(0, ease); if (elapsedTime < duration) { animationFrameId = requestAnimationFrame(animateScroll); } else { setIsScrolling(false); } }; animationFrameId = requestAnimationFrame(animateScroll); }; const isMobile = () => { const regex = /Mobi|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i; return regex.test(navigator.userAgent); }; const handleWheel = (event: WheelEvent) => { if (isMobile()) return; event.preventDefault(); if (isScrolling()) { if (animationFrameId !== null) { cancelAnimationFrame(animationFrameId); } } requestAnimationFrame(() => { smoothScroll(event.deltaY); setIsScrolling(false); }); setIsScrolling(true); }; onCleanup(() => { if (animationFrameId !== null) { cancelAnimationFrame(animationFrameId); } }); return (
{props.children}
); }