手写useSwipe函数,用于处理滑动手势事件

103 阅读1分钟

这个函数接受一个 HTMLElement 的 Ref 作为参数,以及一个可选的 Options 参数,Options 参数包含了各种事件钩子,可以在滑动过程中执行阻止默认事件的操作。

在函数中,主要做了以下事情:

  1. 使用 ref 来创建跟踪滑动起点、终点、是否正在滑动等状态的变量。
  2. 使用 computed 来计算滑动的距离(x 和 y 方向上的变化)以及滑动的方向(left、right、up、down)。
  3. 定义了 onStartonMoveonEnd 函数,分别处理滑动开始、滑动过程和滑动结束的逻辑,并在这些函数中调用了用户提供的事件钩子(beforeStart、afterStart 等)。
  4. 在组件挂载时(onMounted)添加了滑动事件监听器,并在组件卸载时(onUnmounted)移除了这些监听器,以确保事件处理不会发生内存泄漏。

最后,函数返回了 swiping(是否正在滑动)、direction(滑动方向)和 distance(滑动距离)等值,在Vue组件中使用。

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)
        e.preventDefault()
        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,
    }
}