@kiwicom/orbit-components
Version:
Orbit-components is a React component library which provides developers with the easiest possible way of building Kiwi.com’s products.
113 lines (94 loc) • 3.81 kB
JavaScript
import { useState, useEffect, useCallback } from "react";
import { throttle } from "./helpers";
const timing = 1 / 60 * 1000; // eslint-disable-next-line no-bitwise
const decay = v => -0.1 * (1 / timing ^ 4) + v;
const TRIGGER_OFFSET = 50;
const useScroll = ref => {
const [clickStartX, setClickStartX] = useState();
const [scrollStartX, setScrollStartX] = useState();
const [isDragging, setIsDragging] = useState(false);
const [direction, setDirection] = useState(0);
const [momentum, setMomentum] = useState(0);
const [lastScrollX, setLastScrollX] = useState(0);
const [speed, setSpeed] = useState(0);
const [reachedStart, setReachedStart] = useState(false);
const [reachedEnd, setReachedEnd] = useState(false);
const scrollWrapperCurrent = ref.current;
const handleLastScrollX = useCallback(x => setLastScrollX(x), []);
const handleMomentum = throttle(nextMomentum => {
setMomentum(nextMomentum);
if (scrollWrapperCurrent) {
scrollWrapperCurrent.scrollLeft += nextMomentum * timing * direction;
}
}, timing);
useEffect(() => {
if (direction !== 0) {
if (momentum > 0.1 && !isDragging) {
handleMomentum(decay(momentum));
} else if (isDragging) {
setMomentum(speed);
} else {
setDirection(0);
}
}
}, [momentum, isDragging, speed, direction, handleMomentum]);
useEffect(() => {
if (ref.current) {
const handleDragStart = e => {
var _ref$current;
setClickStartX(e.screenX);
setScrollStartX((_ref$current = ref.current) == null ? void 0 : _ref$current.scrollLeft);
setDirection(0);
};
const handleDragMove = e => {
e.preventDefault();
e.stopPropagation();
if (clickStartX !== undefined && scrollStartX !== undefined && ref.current) {
const touchDelta = clickStartX - e.screenX; // eslint-disable-next-line no-param-reassign
ref.current.scrollLeft = scrollStartX + touchDelta;
if (Math.abs(touchDelta) > 1) {
setIsDragging(true);
setDirection(touchDelta / Math.abs(touchDelta));
setSpeed(Math.abs((lastScrollX - e.screenX) / timing));
handleLastScrollX(e.screenX);
}
}
};
const handleDragEnd = () => {
if (isDragging && clickStartX !== undefined) {
var _ref$current2, _ref$current3;
setClickStartX(undefined);
setScrollStartX(undefined);
setIsDragging(false);
if (scrollStartX !== undefined && (_ref$current2 = ref.current) != null && _ref$current2.scrollLeft) {
if (ref.current.scrollLeft <= scrollStartX + TRIGGER_OFFSET) {
setReachedEnd(true);
} else {
setReachedEnd(false);
}
}
if (((_ref$current3 = ref.current) == null ? void 0 : _ref$current3.scrollLeft) === 0) setReachedStart(true);else setReachedStart(false);
}
}; // $FlowFixMe: on mobile browser is null, on desktop is undefined
if (ref.current && ref.current.ontouchstart === undefined) {
// eslint-disable-next-line no-param-reassign
ref.current.onmousedown = handleDragStart; // eslint-disable-next-line no-param-reassign
ref.current.onmousemove = handleDragMove; // eslint-disable-next-line no-param-reassign
ref.current.onmouseup = handleDragEnd; // eslint-disable-next-line no-param-reassign
ref.current.onmouseleave = handleDragEnd;
}
}
}, [scrollWrapperCurrent, clickStartX, isDragging, scrollStartX, handleLastScrollX, lastScrollX]);
return {
clickStartX,
scrollStartX,
isDragging,
direction,
momentum,
lastScrollX,
speed,
reachedEnd,
reachedStart
};
};
export default useScroll;