phanpy/src/utils/useScroll.js

96 lines
2.9 KiB
JavaScript
Raw Normal View History

2023-06-14 11:15:25 +03:00
import { useLayoutEffect, useState } from 'preact/hooks';
export default function useScroll({
2023-02-28 16:56:41 +03:00
scrollableRef,
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',
distanceFromStartPx: _distanceFromStartPx,
distanceFromEndPx: _distanceFromEndPx,
} = {}) {
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;
if (!scrollableElement) return {};
2023-01-14 14:42:04 +03:00
let previousScrollStart = isVertical
? scrollableElement.scrollTop
: scrollableElement.scrollLeft;
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);
const distanceFromStartPx =
_distanceFromStartPx ||
Math.min(
clientDimension * distanceFromStart,
scrollDimension,
scrollStart,
);
const distanceFromEndPx =
_distanceFromEndPx ||
Math.min(
clientDimension * distanceFromEnd,
scrollDimension,
scrollDimension - scrollStart - clientDimension,
);
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;
}
setReachStart(scrollStart <= 0);
2023-01-14 14:42:04 +03:00
setReachEnd(scrollStart + clientDimension >= scrollDimension);
setNearReachStart(scrollStart <= distanceFromStartPx);
setNearReachEnd(
scrollStart + clientDimension >= scrollDimension - distanceFromEndPx,
);
}
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-14 14:42:04 +03:00
return {
scrollDirection,
reachStart,
reachEnd,
nearReachStart,
nearReachEnd,
init: () => {
2023-02-28 16:56:41 +03:00
if (scrollableRef.current) {
scrollableRef.current.dispatchEvent(new Event('scroll'));
}
},
2023-01-14 14:42:04 +03:00
};
}