93 lines
2.3 KiB
JavaScript
93 lines
2.3 KiB
JavaScript
import { NEXT, PREV } from '../../direction'
|
|
import {
|
|
addStartEventListener,
|
|
removeStartEventListener,
|
|
addMoveEventListener,
|
|
removeMoveEventListener,
|
|
addEndEventListener,
|
|
removeEndEventListener,
|
|
} from './event'
|
|
import { createDispatcher } from '../../utils/event'
|
|
import { SWIPE_MIN_DURATION_MS, SWIPE_MIN_DISTANCE_PX } from '../../units'
|
|
|
|
function getCoords(event) {
|
|
if ('TouchEvent' in window && event instanceof TouchEvent) {
|
|
const touch = event.touches[0]
|
|
return {
|
|
x: touch ? touch.clientX : 0,
|
|
y: touch ? touch.clientY : 0,
|
|
}
|
|
}
|
|
return {
|
|
x: event.clientX,
|
|
y: event.clientY,
|
|
}
|
|
}
|
|
|
|
export function swipeable(node, { thresholdProvider }) {
|
|
const dispatch = createDispatcher(node)
|
|
let x
|
|
let y
|
|
let moved = 0
|
|
let swipeStartedAt
|
|
let isTouching = false
|
|
|
|
function isValidSwipe() {
|
|
const swipeDurationMs = Date.now() - swipeStartedAt
|
|
return swipeDurationMs >= SWIPE_MIN_DURATION_MS && Math.abs(moved) >= SWIPE_MIN_DISTANCE_PX
|
|
}
|
|
|
|
function handleDown(event) {
|
|
swipeStartedAt = Date.now()
|
|
moved = 0
|
|
isTouching = true
|
|
const coords = getCoords(event)
|
|
x = coords.x
|
|
y = coords.y
|
|
dispatch('swipeStart', { x, y })
|
|
addMoveEventListener(window, handleMove)
|
|
addEndEventListener(window, handleUp)
|
|
}
|
|
|
|
function handleMove(event) {
|
|
if (!isTouching) return
|
|
const coords = getCoords(event)
|
|
const dx = coords.x - x
|
|
const dy = coords.y - y
|
|
x = coords.x
|
|
y = coords.y
|
|
dispatch('swipeMove', { x, y, dx, dy })
|
|
|
|
if (dx !== 0 && Math.sign(dx) !== Math.sign(moved)) {
|
|
moved = 0
|
|
}
|
|
moved += dx
|
|
if (Math.abs(moved) > thresholdProvider()) {
|
|
dispatch('swipeThresholdReached', { direction: moved > 0 ? PREV : NEXT })
|
|
removeEndEventListener(window, handleUp)
|
|
removeMoveEventListener(window, handleMove)
|
|
}
|
|
}
|
|
|
|
function handleUp(event) {
|
|
removeEndEventListener(window, handleUp)
|
|
removeMoveEventListener(window, handleMove)
|
|
|
|
isTouching = false
|
|
|
|
if (!isValidSwipe()) {
|
|
dispatch('swipeFailed')
|
|
return
|
|
}
|
|
const coords = getCoords(event)
|
|
dispatch('swipeEnd', { x: coords.x, y: coords.y })
|
|
}
|
|
|
|
addStartEventListener(node, handleDown)
|
|
return {
|
|
destroy() {
|
|
removeStartEventListener(node, handleDown)
|
|
},
|
|
}
|
|
}
|