Skip to content

Instantly share code, notes, and snippets.

@devjmetivier
Last active January 24, 2024 19:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devjmetivier/6e4c888d14426bf18442cf91fd1353e1 to your computer and use it in GitHub Desktop.
Save devjmetivier/6e4c888d14426bf18442cf91fd1353e1 to your computer and use it in GitHub Desktop.
import { useEffect, useRef } from 'react';
export type UseLongPressOpts = {
callback: () => void;
delay: number;
distanceThreshold?: number;
};
export function useLongPress({ callback, delay, distanceThreshold = 40 }: UseLongPressOpts) {
const timerRef = useRef<NodeJS.Timeout | void>();
const moveStartXRef = useRef<number>();
const moveStartYRef = useRef<number>();
function handleMouseMove() {
document.addEventListener('mousemove', (e) => {
const { clientX, clientY } = e;
if (!moveStartXRef.current && !moveStartYRef.current) {
moveStartXRef.current = clientX;
moveStartYRef.current = clientY;
}
const distance = Math.hypot(clientX - moveStartXRef.current!, clientY - moveStartYRef.current!);
if (distance > distanceThreshold) {
timerRef.current && clearTimeout(timerRef.current);
document.removeEventListener('mousemove', handleMouseMove);
}
});
}
function handleTouchMove() {
document.addEventListener('touchmove', (e) => {
const { clientX, clientY } = e.touches[0]!;
if (!moveStartXRef.current && !moveStartYRef.current) {
moveStartXRef.current = clientX;
moveStartYRef.current = clientY;
}
const distance = Math.hypot(clientX - moveStartXRef.current!, clientY - moveStartYRef.current!);
if (distance > distanceThreshold) {
timerRef.current && clearTimeout(timerRef.current);
document.removeEventListener('touchmove', handleTouchMove);
}
});
}
function startPressTimer() {
timerRef.current = setTimeout(callback, delay);
}
function clearPressTimerAndCoordRefs() {
timerRef.current && clearTimeout(timerRef.current);
moveStartXRef.current = undefined;
moveStartYRef.current = undefined;
}
function onMouseDown() {
startPressTimer();
document.addEventListener('mousemove', handleMouseMove);
}
function onMouseUp() {
clearPressTimerAndCoordRefs();
document.removeEventListener('mousemove', handleMouseMove);
}
function onTouchStart() {
startPressTimer();
document.addEventListener('touchmove', handleTouchMove);
}
function onTouchEnd() {
clearPressTimerAndCoordRefs();
document.removeEventListener('touchmove', handleTouchMove);
}
useEffect(() => {
return () => {
clearPressTimerAndCoordRefs();
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('touchmove', handleTouchMove);
};
}, []);
return {
onMouseDown,
onMouseUp,
onTouchEnd,
onTouchStart,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment