49 lines
1.5 KiB
TypeScript
49 lines
1.5 KiB
TypeScript
interface SwipeOptions {
|
|
onSwipeLeft?: () => void;
|
|
onSwipeRight?: () => void;
|
|
ignoreSelector?: string;
|
|
minDistance?: number;
|
|
maxDuration?: number;
|
|
}
|
|
|
|
export function attachSwipeHandler(el: HTMLElement, options: SwipeOptions): () => void {
|
|
const minDistance = options.minDistance ?? 60;
|
|
const maxDuration = options.maxDuration ?? 600;
|
|
let startX = 0;
|
|
let startY = 0;
|
|
let startTime = 0;
|
|
let ignored = false;
|
|
|
|
const onTouchStart = (e: TouchEvent) => {
|
|
if (e.touches.length !== 1) {
|
|
ignored = true;
|
|
return;
|
|
}
|
|
const target = e.target as Element | null;
|
|
ignored = !!(options.ignoreSelector && target?.closest(options.ignoreSelector));
|
|
startX = e.touches[0].clientX;
|
|
startY = e.touches[0].clientY;
|
|
startTime = Date.now();
|
|
};
|
|
|
|
const onTouchEnd = (e: TouchEvent) => {
|
|
if (ignored) return;
|
|
const touch = e.changedTouches[0];
|
|
if (!touch) return;
|
|
const dx = touch.clientX - startX;
|
|
const dy = touch.clientY - startY;
|
|
if (Date.now() - startTime > maxDuration) return;
|
|
if (Math.abs(dx) < minDistance) return;
|
|
if (Math.abs(dy) >= Math.abs(dx)) return;
|
|
if (dx < 0) options.onSwipeLeft?.();
|
|
else options.onSwipeRight?.();
|
|
};
|
|
|
|
el.addEventListener("touchstart", onTouchStart, { passive: true });
|
|
el.addEventListener("touchend", onTouchEnd, { passive: true });
|
|
|
|
return () => {
|
|
el.removeEventListener("touchstart", onTouchStart);
|
|
el.removeEventListener("touchend", onTouchEnd);
|
|
};
|
|
}
|