phanpy/src/utils/useScrollFn.js

154 wiersze
4.4 KiB
JavaScript

import { useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks';
import { useThrottledCallback } from 'use-debounce';
export default function useScrollFn(
{
scrollableRef,
distanceFromStart = 1, // ratio of clientHeight/clientWidth
distanceFromEnd = 1, // ratio of clientHeight/clientWidth
scrollThresholdStart = 10,
scrollThresholdEnd = 10,
direction = 'vertical',
distanceFromStartPx: _distanceFromStartPx,
distanceFromEndPx: _distanceFromEndPx,
init,
} = {},
callback,
deps,
) {
if (!callback) return;
// const [scrollDirection, setScrollDirection] = useState(null);
// const [reachStart, setReachStart] = useState(false);
// const [reachEnd, setReachEnd] = useState(false);
// const [nearReachStart, setNearReachStart] = useState(false);
// const [nearReachEnd, setNearReachEnd] = useState(false);
const isVertical = direction === 'vertical';
const previousScrollStart = useRef(null);
const scrollDirection = useRef(null);
const onScroll = useThrottledCallback(() => {
// let scrollDirection = null;
let reachStart = false;
let reachEnd = false;
let nearReachStart = false;
let nearReachEnd = false;
const scrollableElement = scrollableRef.current;
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.current);
const distanceFromStartPx =
_distanceFromStartPx ||
Math.min(
clientDimension * distanceFromStart,
scrollDimension,
scrollStart,
);
const distanceFromEndPx =
_distanceFromEndPx ||
Math.min(
clientDimension * distanceFromEnd,
scrollDimension,
scrollDimension - scrollStart - clientDimension,
);
if (
scrollDistance >=
(previousScrollStart.current < scrollStart
? scrollThresholdEnd
: scrollThresholdStart)
) {
// setScrollDirection(
// previousScrollStart.current < scrollStart ? 'end' : 'start',
// );
scrollDirection.current =
previousScrollStart.current < scrollStart ? 'end' : 'start';
previousScrollStart.current = scrollStart;
}
// setReachStart(scrollStart <= 0);
// setReachEnd(scrollStart + clientDimension >= scrollDimension);
// setNearReachStart(scrollStart <= distanceFromStartPx);
// setNearReachEnd(
// scrollStart + clientDimension >= scrollDimension - distanceFromEndPx,
// );
reachStart = scrollStart <= 0;
reachEnd = scrollStart + clientDimension >= scrollDimension;
nearReachStart = scrollStart <= distanceFromStartPx;
nearReachEnd =
scrollStart + clientDimension >= scrollDimension - distanceFromEndPx;
callback({
scrollDirection: scrollDirection.current,
reachStart,
reachEnd,
nearReachStart,
nearReachEnd,
});
}, 500);
useLayoutEffect(() => {
const scrollableElement = scrollableRef.current;
if (!scrollableElement) return {};
previousScrollStart.current =
scrollableElement[isVertical ? 'scrollTop' : 'scrollLeft'];
scrollableElement.addEventListener('scroll', onScroll, { passive: true });
return () => scrollableElement.removeEventListener('scroll', onScroll);
}, [
distanceFromStart,
distanceFromEnd,
scrollThresholdStart,
scrollThresholdEnd,
...deps,
]);
// useEffect(() => {
// callback({
// scrollDirection,
// reachStart,
// reachEnd,
// nearReachStart,
// nearReachEnd,
// });
// }, [
// scrollDirection,
// reachStart,
// reachEnd,
// nearReachStart,
// nearReachEnd,
// ...deps,
// ]);
useEffect(() => {
if (init && scrollableRef.current) {
queueMicrotask(() => {
scrollableRef.current.dispatchEvent(new Event('scroll'));
});
}
}, [init]);
// return {
// scrollDirection,
// reachStart,
// reachEnd,
// nearReachStart,
// nearReachEnd,
// init: () => {
// if (scrollableRef.current) {
// scrollableRef.current.dispatchEvent(new Event('scroll'));
// }
// },
// };
}