mirror of
https://github.com/gosticks/plane.git
synced 2025-10-16 12:45:33 +00:00
123 lines
3.6 KiB
TypeScript
123 lines
3.6 KiB
TypeScript
import { RefObject, useEffect, useRef } from "react";
|
|
|
|
const SCROLL_BY = 3;
|
|
|
|
const AUTO_SCROLL_THRESHOLD = 15;
|
|
const MAX_SPEED_THRESHOLD = 5;
|
|
|
|
export const useAutoScroller = (
|
|
containerRef: RefObject<HTMLDivElement>,
|
|
shouldScroll = false,
|
|
leftOffset = 0,
|
|
topOffset = 0
|
|
) => {
|
|
const containerDimensions = useRef<DOMRect | undefined>();
|
|
const intervalId = useRef<ReturnType<typeof setInterval> | undefined>(undefined);
|
|
const mousePosition = useRef<{ clientX: number; clientY: number } | undefined>(undefined);
|
|
|
|
const clearRegisteredTimeout = () => {
|
|
clearInterval(intervalId.current);
|
|
};
|
|
|
|
const onDragEnd = () => clearRegisteredTimeout();
|
|
|
|
const handleAutoScroll = (e: MouseEvent) => {
|
|
const rect = containerDimensions.current;
|
|
clearInterval(intervalId.current);
|
|
|
|
if (!rect || !shouldScroll || (e.clientX === 0 && e.clientY === 0)) return;
|
|
|
|
let diffX = 0,
|
|
diffY = 0;
|
|
|
|
if (mousePosition.current) {
|
|
diffX = e.clientX - mousePosition.current.clientX;
|
|
diffY = e.clientY - mousePosition.current.clientY;
|
|
}
|
|
|
|
mousePosition.current = { clientX: e.clientX, clientY: e.clientY };
|
|
const { left, top, width, height } = rect;
|
|
|
|
const mouseX = e.clientX - left - leftOffset;
|
|
const mouseY = e.clientY - top - topOffset;
|
|
|
|
const currWidth = width - leftOffset;
|
|
const currHeight = height - topOffset;
|
|
|
|
// Get Threshold in percentages
|
|
const thresholdX = (currWidth / 100) * AUTO_SCROLL_THRESHOLD;
|
|
const thresholdY = (currHeight / 100) * AUTO_SCROLL_THRESHOLD;
|
|
const maxSpeedX = (currWidth / 100) * MAX_SPEED_THRESHOLD;
|
|
const maxSpeedY = (currHeight / 100) * MAX_SPEED_THRESHOLD;
|
|
|
|
let scrollByX = 0,
|
|
scrollByY = 0;
|
|
|
|
// Check mouse positions against thresholds
|
|
if (mouseX < thresholdX && diffX <= 0) {
|
|
scrollByX = -1 * SCROLL_BY;
|
|
if (mouseX < maxSpeedX) {
|
|
scrollByX *= 2;
|
|
}
|
|
}
|
|
|
|
if (mouseX > currWidth - thresholdX && diffX >= 0) {
|
|
scrollByX = SCROLL_BY;
|
|
if (mouseX > currWidth - maxSpeedX) {
|
|
scrollByX *= 2;
|
|
}
|
|
}
|
|
|
|
if (mouseY < thresholdY && diffY <= 0) {
|
|
scrollByY = -1 * SCROLL_BY;
|
|
if (mouseX < maxSpeedY) {
|
|
scrollByY *= 2;
|
|
}
|
|
}
|
|
|
|
if (mouseY > currHeight - thresholdY && diffY >= 0) {
|
|
scrollByY = SCROLL_BY;
|
|
if (mouseY > currHeight - maxSpeedY) {
|
|
scrollByY *= 2;
|
|
}
|
|
}
|
|
|
|
// if mouse position breaches threshold, then start to scroll
|
|
if (scrollByX || scrollByY) {
|
|
containerRef.current?.scrollBy(scrollByX, scrollByY);
|
|
intervalId.current = setInterval(() => {
|
|
containerRef.current?.scrollBy(scrollByX, scrollByY);
|
|
}, 16);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
const containerElement = containerRef.current;
|
|
|
|
if (!containerElement || !shouldScroll) return;
|
|
|
|
containerElement.addEventListener("drag", handleAutoScroll);
|
|
containerElement.addEventListener("mousemove", handleAutoScroll);
|
|
document.addEventListener("mouseup", onDragEnd);
|
|
document.addEventListener("dragend", onDragEnd);
|
|
|
|
return () => {
|
|
containerElement?.removeEventListener("drag", handleAutoScroll);
|
|
containerElement?.removeEventListener("mousemove", handleAutoScroll);
|
|
document.removeEventListener("mouseup", onDragEnd);
|
|
document.removeEventListener("dragend", onDragEnd);
|
|
};
|
|
}, [shouldScroll, intervalId]);
|
|
|
|
useEffect(() => {
|
|
const containerElement = containerRef.current;
|
|
|
|
if (!containerElement || !shouldScroll) {
|
|
clearRegisteredTimeout();
|
|
containerDimensions.current = undefined;
|
|
}
|
|
|
|
containerDimensions.current = containerElement?.getBoundingClientRect();
|
|
}, [shouldScroll]);
|
|
};
|