vuetify
Version:
Vue.js 2 Semantic Component Framework
142 lines (118 loc) • 4.11 kB
text/typescript
import { VNodeDirective, VNode } from 'vue/types/vnode'
import { keys } from '../util/helpers'
export interface TouchStoredHandlers {
touchstart: (e: TouchEvent) => void
touchend: (e: TouchEvent) => void
touchmove: (e: TouchEvent) => void
}
interface TouchHandlers {
start?: (wrapperEvent: TouchEvent & TouchWrapper) => void
end?: (wrapperEvent: TouchEvent & TouchWrapper) => void
move?: (wrapperEvent: TouchEvent & TouchWrapper) => void
left?: (wrapper: TouchWrapper) => void
right?: (wrapper: TouchWrapper) => void
up?: (wrapper: TouchWrapper) => void
down?: (wrapper: TouchWrapper) => void
}
interface TouchWrapper extends TouchHandlers {
touchstartX: number
touchstartY: number
touchmoveX: number
touchmoveY: number
touchendX: number
touchendY: number
offsetX: number
offsetY: number
}
interface TouchVNodeDirective extends VNodeDirective {
value: TouchHandlers & {
parent?: boolean
options?: AddEventListenerOptions
}
}
const handleGesture = (wrapper: TouchWrapper) => {
const { touchstartX, touchendX, touchstartY, touchendY } = wrapper
const dirRatio = 0.5
const minDistance = 16
wrapper.offsetX = touchendX - touchstartX
wrapper.offsetY = touchendY - touchstartY
if (Math.abs(wrapper.offsetY) < dirRatio * Math.abs(wrapper.offsetX)) {
wrapper.left && (touchendX < touchstartX - minDistance) && wrapper.left(wrapper)
wrapper.right && (touchendX > touchstartX + minDistance) && wrapper.right(wrapper)
}
if (Math.abs(wrapper.offsetX) < dirRatio * Math.abs(wrapper.offsetY)) {
wrapper.up && (touchendY < touchstartY - minDistance) && wrapper.up(wrapper)
wrapper.down && (touchendY > touchstartY + minDistance) && wrapper.down(wrapper)
}
}
function touchstart (event: TouchEvent, wrapper: TouchWrapper) {
const touch = event.changedTouches[0]
wrapper.touchstartX = touch.clientX
wrapper.touchstartY = touch.clientY
wrapper.start &&
wrapper.start(Object.assign(event, wrapper))
}
function touchend (event: TouchEvent, wrapper: TouchWrapper) {
const touch = event.changedTouches[0]
wrapper.touchendX = touch.clientX
wrapper.touchendY = touch.clientY
wrapper.end &&
wrapper.end(Object.assign(event, wrapper))
handleGesture(wrapper)
}
function touchmove (event: TouchEvent, wrapper: TouchWrapper) {
const touch = event.changedTouches[0]
wrapper.touchmoveX = touch.clientX
wrapper.touchmoveY = touch.clientY
wrapper.move && wrapper.move(Object.assign(event, wrapper))
}
function createHandlers (value: TouchHandlers): TouchStoredHandlers {
const wrapper = {
touchstartX: 0,
touchstartY: 0,
touchendX: 0,
touchendY: 0,
touchmoveX: 0,
touchmoveY: 0,
offsetX: 0,
offsetY: 0,
left: value.left,
right: value.right,
up: value.up,
down: value.down,
start: value.start,
move: value.move,
end: value.end
}
return {
touchstart: (e: TouchEvent) => touchstart(e, wrapper),
touchend: (e: TouchEvent) => touchend(e, wrapper),
touchmove: (e: TouchEvent) => touchmove(e, wrapper)
}
}
function inserted (el: HTMLElement, binding: TouchVNodeDirective, vnode: VNode) {
const value = binding.value
const target = value.parent ? el.parentElement : el
const options = value.options || { passive: true }
// Needed to pass unit tests
if (!target) return
const handlers = createHandlers(binding.value)
target._touchHandlers = Object(target._touchHandlers)
target._touchHandlers![vnode.context!._uid] = handlers
keys(handlers).forEach(eventName => {
target.addEventListener(eventName, handlers[eventName] as EventListener, options)
})
}
function unbind (el: HTMLElement, binding: TouchVNodeDirective, vnode: VNode) {
const target = binding.value.parent ? el.parentElement : el
if (!target || !target._touchHandlers) return
const handlers = target._touchHandlers[vnode.context!._uid]
keys(handlers).forEach(eventName => {
target.removeEventListener(eventName, handlers[eventName])
})
delete target._touchHandlers[vnode.context!._uid]
}
export default {
inserted,
unbind
}