File size: 1,759 Bytes
cb6a2d8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 'use client';
/**
* Touch gesture utilities for mobile interactions.
*/
export function hapticFeedback(
style: 'light' | 'medium' | 'heavy' = 'light'
): void {
if (typeof navigator === 'undefined') return;
if (!('vibrate' in navigator)) return;
const patterns: Record<string, number> = {
light: 10,
medium: 20,
heavy: 40,
};
navigator.vibrate(patterns[style] || 10);
}
export interface SwipeResult {
direction: 'up' | 'down' | 'left' | 'right';
distance: number;
}
/**
* Create a swipe detector for an element.
*/
export function createSwipeDetector(
element: HTMLElement,
onSwipe: (result: SwipeResult) => void,
threshold: number = 50
): () => void {
let startX = 0;
let startY = 0;
function handleTouchStart(e: TouchEvent) {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
}
function handleTouchEnd(e: TouchEvent) {
const endX = e.changedTouches[0].clientX;
const endY = e.changedTouches[0].clientY;
const diffX = endX - startX;
const diffY = endY - startY;
const absDiffX = Math.abs(diffX);
const absDiffY = Math.abs(diffY);
if (absDiffX < threshold && absDiffY < threshold) return;
if (absDiffX > absDiffY) {
onSwipe({
direction: diffX > 0 ? 'right' : 'left',
distance: absDiffX,
});
} else {
onSwipe({
direction: diffY > 0 ? 'down' : 'up',
distance: absDiffY,
});
}
}
element.addEventListener('touchstart', handleTouchStart, { passive: true });
element.addEventListener('touchend', handleTouchEnd, { passive: true });
return () => {
element.removeEventListener('touchstart', handleTouchStart);
element.removeEventListener('touchend', handleTouchEnd);
};
}
|