kinetic-slider
Version:
A WebGL-powered kinetic slider component using PIXI.js
1 lines • 12.5 kB
Source Map (JSON)
{"version":3,"file":"useTouchSwipe.cjs","sources":["../../../src/hooks/useTouchSwipe.ts"],"sourcesContent":["import { useEffect, useRef, type RefObject } from \"react\";\nimport ResourceManager from '../managers/ResourceManager';\n\n// Development environment check\nconst isDevelopment = import.meta.env?.MODE === 'development';\n\ninterface UseTouchSwipeProps {\n sliderRef: RefObject<HTMLDivElement | null>;\n onSwipeLeft: () => void;\n onSwipeRight: () => void;\n threshold?: number; // Optional override for swipe threshold\n thresholdFraction?: number; // Optional threshold as fraction of screen width\n minSwipeDistance?: number; // Minimum swipe distance in pixels\n resourceManager?: ResourceManager | null;\n}\n\n/**\n * Hook to handle touch swipe gestures for the slider\n * Detects left and right swipes on touch devices and triggers navigation\n */\nconst useTouchSwipe = ({\n sliderRef,\n onSwipeLeft,\n onSwipeRight,\n threshold,\n thresholdFraction = 0.2, // Default: 20% of screen width\n minSwipeDistance = 30, // Default minimum swipe distance in pixels\n resourceManager\n }: UseTouchSwipeProps) => {\n // Store touch state in refs to avoid re-renders\n const touchStateRef = useRef({\n touchStartX: 0,\n touchStartY: 0,\n touchEndX: 0,\n touchEndY: 0,\n swiping: false\n });\n\n useEffect(() => {\n // Skip during server-side rendering\n if (typeof window === 'undefined') return;\n\n const slider = sliderRef.current;\n if (!slider) {\n if (isDevelopment) {\n console.warn('useTouchSwipe: Slider reference is not available');\n }\n return;\n }\n\n /**\n * Calculate the appropriate swipe threshold based on slider width\n * @returns The calculated threshold in pixels\n */\n const calculateThreshold = (): number => {\n // If explicit threshold is provided, use it\n if (threshold !== undefined) return threshold;\n\n // Calculate based on slider width if available, otherwise use window width\n const baseWidth = slider.clientWidth || window.innerWidth;\n const calculatedThreshold = baseWidth * thresholdFraction;\n\n // Ensure minimum threshold for small screens\n return Math.max(calculatedThreshold, minSwipeDistance);\n };\n\n /**\n * Handle the start of a touch event\n * @param e - The touch event\n */\n const handleTouchStart = (e: Event) => {\n try {\n // Cast to TouchEvent for type safety\n const touchEvent = e as TouchEvent;\n\n if (!touchEvent.touches || touchEvent.touches.length === 0) return;\n\n // Store initial touch position\n touchStateRef.current.touchStartX = touchEvent.touches[0].clientX;\n touchStateRef.current.touchStartY = touchEvent.touches[0].clientY;\n touchStateRef.current.swiping = true;\n\n if (isDevelopment) {\n console.log(`Touch start at (${touchStateRef.current.touchStartX}, ${touchStateRef.current.touchStartY})`);\n }\n } catch (error) {\n if (isDevelopment) {\n console.error('Error in touch start handler:', error);\n }\n }\n };\n\n /**\n * Handle touch move events\n * @param e - The touch event\n */\n const handleTouchMove = (e: Event) => {\n try {\n // Cast to TouchEvent for type safety\n const touchEvent = e as TouchEvent;\n\n if (!touchStateRef.current.swiping || !touchEvent.touches || touchEvent.touches.length === 0) return;\n\n // Update current touch position\n touchStateRef.current.touchEndX = touchEvent.touches[0].clientX;\n touchStateRef.current.touchEndY = touchEvent.touches[0].clientY;\n } catch (error) {\n if (isDevelopment) {\n console.error('Error in touch move handler:', error);\n }\n }\n };\n\n /**\n * Handle the end of a touch event\n */\n const handleTouchEnd = () => {\n try {\n if (!touchStateRef.current.swiping) return;\n\n // Calculate swipe distance and angle\n const { touchStartX, touchStartY, touchEndX, touchEndY } = touchStateRef.current;\n const deltaX = touchEndX - touchStartX;\n const deltaY = touchEndY - touchStartY;\n const swipeThreshold = calculateThreshold();\n\n // Calculate absolute distances\n const absX = Math.abs(deltaX);\n const absY = Math.abs(deltaY);\n\n if (isDevelopment) {\n console.log(`Touch end: deltaX=${deltaX}, deltaY=${deltaY}, threshold=${swipeThreshold}`);\n }\n\n // Only trigger if horizontal distance > vertical distance (to avoid scrolling conflicts)\n // and if the horizontal distance exceeds the threshold\n if (absX > absY && absX > swipeThreshold) {\n // Determine direction and trigger appropriate callback\n if (deltaX < 0) {\n if (isDevelopment) {\n console.log('Swipe left detected');\n }\n onSwipeLeft();\n } else {\n if (isDevelopment) {\n console.log('Swipe right detected');\n }\n onSwipeRight();\n }\n }\n\n // Reset swiping state\n touchStateRef.current.swiping = false;\n } catch (error) {\n if (isDevelopment) {\n console.error('Error in touch end handler:', error);\n }\n // Reset swiping state even on error\n touchStateRef.current.swiping = false;\n }\n };\n\n /**\n * Handle touch cancel events\n */\n const handleTouchCancel = () => {\n try {\n if (isDevelopment) {\n console.log('Touch canceled');\n }\n // Reset swiping state\n touchStateRef.current.swiping = false;\n } catch (error) {\n if (isDevelopment) {\n console.error('Error in touch cancel handler:', error);\n }\n // Reset swiping state even on error\n touchStateRef.current.swiping = false;\n }\n };\n\n // Add event listeners with passive flag for better scrolling performance\n // Use ResourceManager if available, otherwise add directly\n if (resourceManager) {\n resourceManager.addEventListener(slider, \"touchstart\", handleTouchStart);\n resourceManager.addEventListener(slider, \"touchmove\", handleTouchMove);\n resourceManager.addEventListener(slider, \"touchend\", handleTouchEnd);\n resourceManager.addEventListener(slider, \"touchcancel\", handleTouchCancel);\n } else {\n // Use regular event listeners with passive option\n slider.addEventListener(\"touchstart\", handleTouchStart, { passive: true });\n slider.addEventListener(\"touchmove\", handleTouchMove, { passive: true });\n slider.addEventListener(\"touchend\", handleTouchEnd);\n slider.addEventListener(\"touchcancel\", handleTouchCancel);\n }\n\n // Cleanup function\n return () => {\n // Remove event listeners if ResourceManager not used\n if (!resourceManager) {\n slider.removeEventListener(\"touchstart\", handleTouchStart);\n slider.removeEventListener(\"touchmove\", handleTouchMove);\n slider.removeEventListener(\"touchend\", handleTouchEnd);\n slider.removeEventListener(\"touchcancel\", handleTouchCancel);\n }\n // Note: ResourceManager handles event cleanup when disposed\n };\n }, [\n sliderRef,\n onSwipeLeft,\n onSwipeRight,\n threshold,\n thresholdFraction,\n minSwipeDistance,\n resourceManager\n ]);\n\n // No return value needed as this hook just sets up the touch handlers\n};\n\nexport default useTouchSwipe;"],"names":["useRef","useEffect"],"mappings":"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"}