@sv-use/core
Version:
A collection of Svelte 5 utilities.
112 lines (111 loc) • 3.54 kB
JavaScript
import { handleEventListener } from '../handle-event-listener/index.svelte.js';
import { noop, normalizeValue } from '../__internal__/utils.svelte.js';
/**
* Reactive swipe detection for mobile devices.
* @param target The target on which to detect swipe events.
* @param options Additional options to customize the behavior.
* @see https://svelte-librarian.github.io/sv-use/docs/core/on-swipe
*/
export function onSwipe(target, options = {}) {
const { passive = true, threshold = 50, onStart = noop, onMove = noop, onEnd = noop } = options;
const listenerOptions = { passive, capture: !passive };
let cleanups = [];
let isSwiping = $state(false);
let coordsStart = $state({ x: 0, y: 0 });
let coordsEnd = $state({ x: 0, y: 0 });
const _target = $derived(normalizeValue(target));
const diffX = $derived(coordsStart.x - coordsEnd.x);
const diffY = $derived(coordsStart.y - coordsEnd.y);
const isThresholdExceeded = $derived.by(() => {
return Math.max(Math.abs(diffX), Math.abs(diffY)) >= threshold;
});
const direction = $derived.by(() => {
if (!isThresholdExceeded)
return 'none';
if (Math.abs(diffX) > Math.abs(diffY)) {
return diffX > 0 ? 'left' : 'right';
}
else {
return diffY > 0 ? 'up' : 'down';
}
});
$effect(() => {
if (_target) {
cleanups = [
handleEventListener(_target, 'touchstart', onTouchStart, listenerOptions),
handleEventListener(_target, 'touchmove', onTouchMove, listenerOptions),
handleEventListener(_target, ['touchend', 'touchcancel'], onTouchEnd, listenerOptions)
];
}
return cleanup;
});
function getTouchEventCoords(e) {
return [e.touches[0].clientX, e.touches[0].clientY];
}
function updateCoordsStart(x, y) {
coordsStart.x = x;
coordsStart.y = y;
}
function updateCoordsEnd(x, y) {
coordsEnd.x = x;
coordsEnd.y = y;
}
function onTouchStart(event) {
if (event.touches.length !== 1)
return;
const [x, y] = getTouchEventCoords(event);
updateCoordsStart(x, y);
updateCoordsEnd(x, y);
onStart(event);
}
function onTouchMove(event) {
if (event.touches.length !== 1)
return;
const [x, y] = getTouchEventCoords(event);
updateCoordsEnd(x, y);
if (listenerOptions.capture && !listenerOptions.passive && Math.abs(diffX) > Math.abs(diffY)) {
event.preventDefault();
}
if (!isSwiping && isThresholdExceeded) {
isSwiping = true;
}
if (isSwiping) {
onMove(event);
}
}
function onTouchEnd(event) {
if (isSwiping) {
onEnd(event, direction);
}
isSwiping = false;
}
function cleanup() {
cleanups.forEach((fn) => fn());
}
function reset() {
coordsStart = { x: 0, y: 0 };
coordsEnd = { x: 0, y: 0 };
}
return {
get isSwiping() {
return isSwiping;
},
get direction() {
return direction;
},
get coordsStart() {
return coordsStart;
},
get coordsEnd() {
return coordsEnd;
},
get lengthX() {
return diffX;
},
get lengthY() {
return diffY;
},
reset,
cleanup
};
}