this repo has no description
at hotfix/infinite-loop-intersection-observer 95 lines 3.0 kB view raw
1import { useLayoutEffect, useState } from 'preact/hooks'; 2 3export default function useScroll({ 4 scrollableRef, 5 distanceFromStart = 1, // ratio of clientHeight/clientWidth 6 distanceFromEnd = 1, // ratio of clientHeight/clientWidth 7 scrollThresholdStart = 10, 8 scrollThresholdEnd = 10, 9 direction = 'vertical', 10 distanceFromStartPx: _distanceFromStartPx, 11 distanceFromEndPx: _distanceFromEndPx, 12} = {}) { 13 const [scrollDirection, setScrollDirection] = useState(null); 14 const [reachStart, setReachStart] = useState(false); 15 const [reachEnd, setReachEnd] = useState(false); 16 const [nearReachStart, setNearReachStart] = useState(false); 17 const [nearReachEnd, setNearReachEnd] = useState(false); 18 const isVertical = direction === 'vertical'; 19 20 useLayoutEffect(() => { 21 const scrollableElement = scrollableRef.current; 22 if (!scrollableElement) return {}; 23 let previousScrollStart = isVertical 24 ? scrollableElement.scrollTop 25 : scrollableElement.scrollLeft; 26 27 function onScroll() { 28 const { 29 scrollTop, 30 scrollLeft, 31 scrollHeight, 32 scrollWidth, 33 clientHeight, 34 clientWidth, 35 } = scrollableElement; 36 const scrollStart = isVertical ? scrollTop : scrollLeft; 37 const scrollDimension = isVertical ? scrollHeight : scrollWidth; 38 const clientDimension = isVertical ? clientHeight : clientWidth; 39 const scrollDistance = Math.abs(scrollStart - previousScrollStart); 40 const distanceFromStartPx = 41 _distanceFromStartPx || 42 Math.min( 43 clientDimension * distanceFromStart, 44 scrollDimension, 45 scrollStart, 46 ); 47 const distanceFromEndPx = 48 _distanceFromEndPx || 49 Math.min( 50 clientDimension * distanceFromEnd, 51 scrollDimension, 52 scrollDimension - scrollStart - clientDimension, 53 ); 54 55 if ( 56 scrollDistance >= 57 (previousScrollStart < scrollStart 58 ? scrollThresholdEnd 59 : scrollThresholdStart) 60 ) { 61 setScrollDirection(previousScrollStart < scrollStart ? 'end' : 'start'); 62 previousScrollStart = scrollStart; 63 } 64 65 setReachStart(scrollStart <= 0); 66 setReachEnd(scrollStart + clientDimension >= scrollDimension); 67 setNearReachStart(scrollStart <= distanceFromStartPx); 68 setNearReachEnd( 69 scrollStart + clientDimension >= scrollDimension - distanceFromEndPx, 70 ); 71 } 72 73 scrollableElement.addEventListener('scroll', onScroll, { passive: true }); 74 75 return () => scrollableElement.removeEventListener('scroll', onScroll); 76 }, [ 77 distanceFromStart, 78 distanceFromEnd, 79 scrollThresholdStart, 80 scrollThresholdEnd, 81 ]); 82 83 return { 84 scrollDirection, 85 reachStart, 86 reachEnd, 87 nearReachStart, 88 nearReachEnd, 89 init: () => { 90 if (scrollableRef.current) { 91 scrollableRef.current.dispatchEvent(new Event('scroll')); 92 } 93 }, 94 }; 95}