this repo has no description
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}