Vue 3 封装hooks useSwipe

420 阅读1分钟

这是我对手机滑动手势的封装:

  • 接受两个参数:
    element:触发事件的目标元素
    options:可接受六个函数,分别是触摸开始前后、触摸移动前后、触摸结束前后
  • 返回一个对象:
    swiping: Ref<boolean> 表示是否正在移动
    direction: ComputedRef<"left"|"right"|""|"down"|"up"> 表示移动的方向
    distance: ComputedRef<{ x: number; y: number; } | null> 表示移动的距离
import { computed, onMounted, onUnmounted, ref, Ref } from 'vue'

type Point = {
  x: number
  y: number
}

interface Options {
  beforeStart?: (e: TouchEvent) => void
  afterStart?: (e: TouchEvent) => void
  beforeMove?: (e: TouchEvent) => void
  afterMove?: (e: TouchEvent) => void
  beforeEnd?: (e: TouchEvent) => void
  afterEnd?: (e: TouchEvent) => void
}

export const useSwipe = (element: Ref<HTMLElement | undefined>, options?: Options) => {
  const start = ref<Point>()
  const end = ref<Point>()
  const swiping = ref(false)
  const distance = computed(() => {
    if (!start.value || !end.value) {
      return null
    }
    return {
      x: end.value.x - start.value.x,
      y: end.value.y - start.value.y
    }
  })
  const direction = computed(() => {
    if (!distance.value) {
      return ''
    }
    const { x, y } = distance.value
    if (Math.abs(x) > Math.abs(y)) {
      return x > 0 ? 'right' : 'left'
    } else {
      return y > 0 ? 'down' : 'up'
    }
  })
  const onStart = (e: TouchEvent) => {
    options?.beforeStart?.(e)
    swiping.value = true
    end.value = start.value = {
      x: e.touches[0].screenX,
      y: e.touches[0].screenY
    }
    options?.afterStart?.(e)
  }
  const onMove = (e: TouchEvent) => {
    options?.beforeMove?.(e)
    if (!start.value) {
      return
    }
    end.value = { x: e.touches[0].screenX, y: e.touches[0].screenY }
    options?.afterMove?.(e)
  }
  const onEnd = (e: TouchEvent) => {
    options?.beforeEnd?.(e)
    swiping.value = false
    options?.afterEnd?.(e)
  }

  onMounted(() => {
    if (!element.value) {
      return
    }
    element.value.addEventListener('touchstart', onStart)
    element.value.addEventListener('touchmove', onMove)
    element.value.addEventListener('touchend', onEnd)
  })
  onUnmounted(() => {
    if (!element.value) {
      return
    }
    element.value.removeEventListener('touchstart', onStart)
    element.value.removeEventListener('touchmove', onMove)
    element.value.removeEventListener('touchend', onEnd)
  })
  return { swiping, direction, distance }
}