79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
import { useEffect, useRef, useState } from 'react';
|
|
|
|
interface UseScrollDetectionOptions {
|
|
targetElementId: string;
|
|
scrollThreshold?: number;
|
|
onStateChange?: (state: {
|
|
isScrollingPastTarget: boolean;
|
|
isScrollingBack: boolean;
|
|
scrollProgress: number;
|
|
}) => void;
|
|
}
|
|
|
|
interface UseScrollDetectionReturn {
|
|
isScrollingPastTarget: boolean;
|
|
isScrollingBack: boolean;
|
|
scrollProgress: number;
|
|
}
|
|
|
|
export function useScrollDetection({
|
|
targetElementId,
|
|
scrollThreshold = 0.3,
|
|
onStateChange,
|
|
}: UseScrollDetectionOptions): UseScrollDetectionReturn {
|
|
const [isScrollingPastTarget, setIsScrollingPastTarget] = useState(false);
|
|
const [isScrollingBack, setIsScrollingBack] = useState(false);
|
|
const [scrollProgress, setScrollProgress] = useState(0);
|
|
const lastScrollYRef = useRef(0);
|
|
const targetElementRef = useRef<HTMLElement | null>(null);
|
|
|
|
useEffect(() => {
|
|
// Get target element
|
|
targetElementRef.current = document.getElementById(targetElementId);
|
|
|
|
const handleScroll = () => {
|
|
if (!targetElementRef.current) return;
|
|
|
|
const targetRect = targetElementRef.current.getBoundingClientRect();
|
|
const windowHeight = window.innerHeight;
|
|
const scrollY = window.scrollY;
|
|
const currentScrollDirection = scrollY > lastScrollYRef.current ? 'down' : 'up';
|
|
|
|
// Calculate if we've scrolled past the target element
|
|
const targetTop = targetRect.top + scrollY;
|
|
const thresholdDistance = window.innerHeight * scrollThreshold;
|
|
const isPastTarget = scrollY > targetTop - thresholdDistance;
|
|
|
|
// Determine scroll direction
|
|
const isScrollingBackward = currentScrollDirection === 'up';
|
|
|
|
// Calculate scroll progress (0 to 1)
|
|
const progress = Math.min(scrollY / (targetTop + thresholdDistance), 1);
|
|
|
|
setIsScrollingPastTarget(isPastTarget);
|
|
setIsScrollingBack(isScrollingBackward);
|
|
setScrollProgress(progress);
|
|
|
|
// Call callback if provided
|
|
if (onStateChange) {
|
|
onStateChange({
|
|
isScrollingPastTarget: isPastTarget,
|
|
isScrollingBack: isScrollingBackward,
|
|
scrollProgress: progress,
|
|
});
|
|
}
|
|
|
|
lastScrollYRef.current = scrollY;
|
|
};
|
|
|
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
|
}, [targetElementId, scrollThreshold, onStateChange]);
|
|
|
|
return {
|
|
isScrollingPastTarget,
|
|
isScrollingBack,
|
|
scrollProgress,
|
|
};
|
|
}
|