UNPKG

lism

Version:

Collection of TypeScript Utilities to help developers streamline their coding workflow.

94 lines (79 loc) 3.27 kB
import { type Point } from '@lism-internal/shared/interfaces/common'; import { TouchEvent, useCallback, useRef, useState } from 'react'; interface UseTouchLeaveRadiusResult { delta: Point; isLeave: boolean; handleTouchStart: (event: TouchEvent) => void; handleTouchMove: (event: TouchEvent) => void; handleTouchEnd: () => void; } const calculateDistance = (x1: number, y1: number, x2: number, y2: number): number => Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); /** * A custom React hook for detecting if a touch event has left a specified radius. * * @param {number} radius - The radius (in pixels) within which the touch event is considered inside. Must be greater than 0. * @throws {Error} Throws an error if the `radius` parameter is less than or equal to 0. * @returns {object} An object containing: * - `delta`: An object representing the distance moved in the x and y directions. * - `isLeave`: A boolean indicating whether the touch event has left the specified radius. * - `handleTouchStart`: A callback function to handle the start of a touch event. * - `handleTouchMove`: A callback function to handle the movement of a touch event. * - `handleTouchEnd`: A callback function to handle the end of a touch event. * * @example * const ExampleComponent = () => { * const { delta, isLeave, handleTouchStart, handleTouchMove, handleTouchEnd } = useTouchLeaveRadius(20); * return ( * <div * onTouchStart={handleTouchStart} * onTouchMove={handleTouchMove} * onTouchEnd={handleTouchEnd} * > * Touch and move this element in a mobile browser. * <p>Delta: X: {delta.x}, Y: {delta.y}</p> * {isLeave && <p>You have left the specified radius!</p>} * </div> * ); * }; */ const useTouchLeaveRadius = (radius: number): UseTouchLeaveRadiusResult => { if (radius <= 0) { throw new Error('[useTouchLeaveRadius] The radius must be greater than 0.'); } const touchStartRef = useRef<Point>({ x: 0, y: 0 }); const [isLeave, setIsLeave] = useState(false); const [delta, setDelta] = useState<Point>({ x: 0, y: 0 }); const handleTouchStart = useCallback((event: TouchEvent) => { const touch = event.touches[0]; if (!touch) { console.warn('[useTouchLeaveRadius][handleTouchStart] No touch event found.'); return; } const { clientX, clientY } = touch; touchStartRef.current = { x: clientX, y: clientY }; setDelta({ x: 0, y: 0 } as Point); setIsLeave(false); }, []); const handleTouchMove = useCallback( (event: TouchEvent) => { const touch = event.touches[0]; if (!touch) { console.warn('[useTouchLeaveRadius][handleTouchMove] No touch event found.'); return; } const { clientX, clientY } = touch; const { x: startX, y: startY } = touchStartRef.current; setDelta({ x: Math.abs(startX - clientX), y: Math.abs(startY - clientY) } as Point); if (calculateDistance(startX, startY, clientX, clientY) >= radius) { setIsLeave(true); } }, [radius] ); const handleTouchEnd = useCallback(() => { setIsLeave(false); }, []); return { delta, isLeave, handleTouchStart, handleTouchMove, handleTouchEnd }; }; export default useTouchLeaveRadius;