2023-06-14 11:15:25 +03:00
|
|
|
import { useLayoutEffect, useState } from 'preact/hooks';
|
2023-01-02 16:36:24 +03:00
|
|
|
|
|
|
|
export default function useScroll({
|
2023-02-28 16:56:41 +03:00
|
|
|
scrollableRef,
|
2023-01-16 15:32:30 +03:00
|
|
|
distanceFromStart = 1, // ratio of clientHeight/clientWidth
|
|
|
|
distanceFromEnd = 1, // ratio of clientHeight/clientWidth
|
2023-01-14 14:42:04 +03:00
|
|
|
scrollThresholdStart = 10,
|
|
|
|
scrollThresholdEnd = 10,
|
|
|
|
direction = 'vertical',
|
2023-02-25 05:50:02 +03:00
|
|
|
distanceFromStartPx: _distanceFromStartPx,
|
|
|
|
distanceFromEndPx: _distanceFromEndPx,
|
2023-01-02 16:36:24 +03:00
|
|
|
} = {}) {
|
|
|
|
const [scrollDirection, setScrollDirection] = useState(null);
|
2023-01-14 14:42:04 +03:00
|
|
|
const [reachStart, setReachStart] = useState(false);
|
|
|
|
const [reachEnd, setReachEnd] = useState(false);
|
|
|
|
const [nearReachStart, setNearReachStart] = useState(false);
|
|
|
|
const [nearReachEnd, setNearReachEnd] = useState(false);
|
|
|
|
const isVertical = direction === 'vertical';
|
|
|
|
|
2023-06-14 11:15:25 +03:00
|
|
|
useLayoutEffect(() => {
|
2023-02-28 16:56:41 +03:00
|
|
|
const scrollableElement = scrollableRef.current;
|
2023-04-14 10:30:04 +03:00
|
|
|
if (!scrollableElement) return {};
|
2023-01-14 14:42:04 +03:00
|
|
|
let previousScrollStart = isVertical
|
|
|
|
? scrollableElement.scrollTop
|
|
|
|
: scrollableElement.scrollLeft;
|
2023-01-02 16:36:24 +03:00
|
|
|
|
|
|
|
function onScroll() {
|
2023-01-14 14:42:04 +03:00
|
|
|
const {
|
|
|
|
scrollTop,
|
|
|
|
scrollLeft,
|
|
|
|
scrollHeight,
|
|
|
|
scrollWidth,
|
|
|
|
clientHeight,
|
|
|
|
clientWidth,
|
|
|
|
} = scrollableElement;
|
|
|
|
const scrollStart = isVertical ? scrollTop : scrollLeft;
|
|
|
|
const scrollDimension = isVertical ? scrollHeight : scrollWidth;
|
|
|
|
const clientDimension = isVertical ? clientHeight : clientWidth;
|
|
|
|
const scrollDistance = Math.abs(scrollStart - previousScrollStart);
|
2023-02-25 05:50:02 +03:00
|
|
|
const distanceFromStartPx =
|
|
|
|
_distanceFromStartPx ||
|
|
|
|
Math.min(
|
|
|
|
clientDimension * distanceFromStart,
|
|
|
|
scrollDimension,
|
|
|
|
scrollStart,
|
|
|
|
);
|
|
|
|
const distanceFromEndPx =
|
|
|
|
_distanceFromEndPx ||
|
|
|
|
Math.min(
|
|
|
|
clientDimension * distanceFromEnd,
|
|
|
|
scrollDimension,
|
|
|
|
scrollDimension - scrollStart - clientDimension,
|
|
|
|
);
|
2023-01-02 16:36:24 +03:00
|
|
|
|
2023-01-07 09:45:04 +03:00
|
|
|
if (
|
|
|
|
scrollDistance >=
|
2023-01-14 14:42:04 +03:00
|
|
|
(previousScrollStart < scrollStart
|
|
|
|
? scrollThresholdEnd
|
|
|
|
: scrollThresholdStart)
|
2023-01-07 09:45:04 +03:00
|
|
|
) {
|
2023-01-14 14:42:04 +03:00
|
|
|
setScrollDirection(previousScrollStart < scrollStart ? 'end' : 'start');
|
|
|
|
previousScrollStart = scrollStart;
|
2023-01-02 18:16:49 +03:00
|
|
|
}
|
2023-01-02 16:36:24 +03:00
|
|
|
|
2023-01-20 11:05:27 +03:00
|
|
|
setReachStart(scrollStart <= 0);
|
2023-01-14 14:42:04 +03:00
|
|
|
setReachEnd(scrollStart + clientDimension >= scrollDimension);
|
|
|
|
setNearReachStart(scrollStart <= distanceFromStartPx);
|
|
|
|
setNearReachEnd(
|
|
|
|
scrollStart + clientDimension >= scrollDimension - distanceFromEndPx,
|
2023-01-02 16:36:24 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
scrollableElement.addEventListener('scroll', onScroll, { passive: true });
|
|
|
|
|
|
|
|
return () => scrollableElement.removeEventListener('scroll', onScroll);
|
2023-01-07 09:45:04 +03:00
|
|
|
}, [
|
2023-01-14 14:42:04 +03:00
|
|
|
distanceFromStart,
|
|
|
|
distanceFromEnd,
|
|
|
|
scrollThresholdStart,
|
|
|
|
scrollThresholdEnd,
|
2023-01-07 09:45:04 +03:00
|
|
|
]);
|
2023-01-02 16:36:24 +03:00
|
|
|
|
2023-01-14 14:42:04 +03:00
|
|
|
return {
|
|
|
|
scrollDirection,
|
|
|
|
reachStart,
|
|
|
|
reachEnd,
|
|
|
|
nearReachStart,
|
|
|
|
nearReachEnd,
|
2023-01-16 15:32:30 +03:00
|
|
|
init: () => {
|
2023-02-28 16:56:41 +03:00
|
|
|
if (scrollableRef.current) {
|
|
|
|
scrollableRef.current.dispatchEvent(new Event('scroll'));
|
2023-01-16 15:32:30 +03:00
|
|
|
}
|
|
|
|
},
|
2023-01-14 14:42:04 +03:00
|
|
|
};
|
2023-01-02 16:36:24 +03:00
|
|
|
}
|