@supunlakmal/hooks
Version:
A collection of reusable React hooks
110 lines • 5.07 kB
JavaScript
import { useRef, useCallback, useEffect } from 'react';
// Default options
const defaultOptions = {
threshold: 50, // Minimum swipe distance of 50px
};
/**
* Custom hook to detect swipe gestures (left, right, up, down) on a target element.
*
* @param ref - A React ref attached to the target element.
* @param options - Configuration options including swipe callbacks and threshold.
* @param options.threshold - Minimum distance (px) to be considered a swipe (default: 50).
* @param options.onSwipeLeft - Callback for left swipe.
* @param options.onSwipeRight - Callback for right swipe.
* @param options.onSwipeUp - Callback for up swipe.
* @param options.onSwipeDown - Callback for down swipe.
*/
export const useSwipe = (ref, { threshold = defaultOptions.threshold, onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown, } = {}) => {
const touchStartX = useRef(null);
const touchStartY = useRef(null);
const touchEndX = useRef(null);
const touchEndY = useRef(null);
const handleTouchStart = useCallback((event) => {
// Only track single touch swipes
if (event.touches.length === 1) {
touchStartX.current = event.touches[0].clientX;
touchStartY.current = event.touches[0].clientY;
touchEndX.current = event.touches[0].clientX; // Initialize end to start
touchEndY.current = event.touches[0].clientY;
}
}, []);
const handleTouchMove = useCallback((event) => {
// Update end coordinates as the touch moves
if (event.touches.length === 1) {
touchEndX.current = event.touches[0].clientX;
touchEndY.current = event.touches[0].clientY;
}
}, []);
const handleTouchEnd = useCallback((event) => {
if (touchStartX.current === null ||
touchStartY.current === null ||
touchEndX.current === null ||
touchEndY.current === null) {
return; // No swipe detected if start/end points are missing
}
const deltaX = touchEndX.current - touchStartX.current;
const deltaY = touchEndY.current - touchStartY.current;
const absDeltaX = Math.abs(deltaX);
const absDeltaY = Math.abs(deltaY);
// Check if the swipe distance meets the threshold
if (absDeltaX > threshold || absDeltaY > threshold) {
// Determine primary direction (horizontal or vertical)
if (absDeltaX > absDeltaY) {
// Horizontal swipe
if (deltaX > 0) {
onSwipeRight === null || onSwipeRight === void 0 ? void 0 : onSwipeRight(event);
}
else {
onSwipeLeft === null || onSwipeLeft === void 0 ? void 0 : onSwipeLeft(event);
}
}
else {
// Vertical swipe
if (deltaY > 0) {
onSwipeDown === null || onSwipeDown === void 0 ? void 0 : onSwipeDown(event);
}
else {
onSwipeUp === null || onSwipeUp === void 0 ? void 0 : onSwipeUp(event);
}
}
}
// Reset coordinates after swipe ends
touchStartX.current = null;
touchStartY.current = null;
touchEndX.current = null;
touchEndY.current = null;
}, [threshold, onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown]);
// Add event listeners to the ref element
useEffect(() => {
const element = ref.current;
if (element) {
// Use passive listeners for touch events to improve scroll performance
element.addEventListener('touchstart', handleTouchStart, { passive: true });
element.addEventListener('touchmove', handleTouchMove, {
passive: true,
});
element.addEventListener('touchend', handleTouchEnd);
// Optional: Add touchcancel listener to reset state if touch is interrupted
const handleTouchCancel = () => {
touchStartX.current = null;
touchStartY.current = null;
touchEndX.current = null;
touchEndY.current = null;
};
element.addEventListener('touchcancel', handleTouchCancel, { passive: true });
// Cleanup listeners on component unmount or ref change
return () => {
element.removeEventListener('touchstart', handleTouchStart);
element.removeEventListener('touchmove', handleTouchMove);
element.removeEventListener('touchend', handleTouchEnd);
element.removeEventListener('touchcancel', handleTouchCancel);
};
}
// Add explicit return for when the element doesn't exist
return () => {
// No cleanup needed if element didn't exist
};
}, [ref, handleTouchStart, handleTouchMove, handleTouchEnd]);
// This hook doesn't return anything directly, it attaches listeners
};
//# sourceMappingURL=useSwipe.js.map