Skip to content

v-touch

触摸手势检测指令,支持滑动、双指缩放、旋转、点击和长按。

起始版本: 1.2.0

用法

滑动检测

vue
<template>
  <div v-touch="{ onSwipe: handleSwipe }">
    向任意方向滑动
  </div>
</template>

<script setup>
function handleSwipe(direction, event) {
  console.log('滑动方向:', direction) // 'left' | 'right' | 'up' | 'down'
}
</script>

方向性滑动

vue
<template>
  <div v-touch="{ onSwipeLeft: goNext, onSwipeRight: goPrev }">
    左右滑动
  </div>
</template>

点击和长按

vue
<template>
  <div v-touch="{ onTap: handleTap, onLongPress: handleLongPress }">
    点击或长按
  </div>
</template>

<script setup>
function handleTap(event) {
  console.log('点击!')
}

function handleLongPress(event) {
  console.log('长按!')
}
</script>

双指缩放和旋转

vue
<template>
  <div v-touch="{ onPinch: handlePinch, onRotate: handleRotate }">
    使用双指缩放或旋转
  </div>
</template>

<script setup>
import { ref } from 'vue'

const scale = ref(1)
const rotation = ref(0)

function handlePinch(newScale, event) {
  scale.value = newScale
}

function handleRotate(angle, event) {
  rotation.value = angle
}
</script>

API

类型

typescript
type SwipeDirection = 'left' | 'right' | 'up' | 'down'

interface TouchOptions {
  /** 滑动最小距离(像素)@default 30 */
  swipeThreshold?: number
  /** 滑动最大时间(毫秒)@default 500 */
  swipeTimeout?: number
  /** 双指缩放最小变化 @default 0.1 */
  pinchThreshold?: number
  /** 启用滑动检测 @default true */
  enableSwipe?: boolean
  /** 启用双指缩放检测 @default true */
  enablePinch?: boolean
  /** 启用旋转检测 @default true */
  enableRotate?: boolean
  /** 启用点击检测 @default true */
  enableTap?: boolean
  /** 点击最大时间(毫秒)@default 250 */
  tapTimeout?: number
  /** 点击最大移动距离(像素)@default 10 */
  tapThreshold?: number
  /** 启用长按检测 @default true */
  enableLongPress?: boolean
  /** 长按超时时间(毫秒)@default 500 */
  longPressTimeout?: number
  /** 启用桌面端鼠标模拟 @default true */
  enableMouse?: boolean

  /** 滑动回调 */
  onSwipe?: (direction: SwipeDirection, event: TouchEvent | MouseEvent) => void
  /** 向左滑动回调 */
  onSwipeLeft?: (event: TouchEvent | MouseEvent) => void
  /** 向右滑动回调 */
  onSwipeRight?: (event: TouchEvent | MouseEvent) => void
  /** 向上滑动回调 */
  onSwipeUp?: (event: TouchEvent | MouseEvent) => void
  /** 向下滑动回调 */
  onSwipeDown?: (event: TouchEvent | MouseEvent) => void
  /** 双指缩放回调 */
  onPinch?: (scale: number, event: TouchEvent) => void
  /** 旋转回调 */
  onRotate?: (angle: number, event: TouchEvent) => void
  /** 点击回调 */
  onTap?: (event: TouchEvent | MouseEvent) => void
  /** 长按回调 */
  onLongPress?: (event: TouchEvent | MouseEvent) => void
  /** 触摸开始回调 */
  onTouchStart?: (event: TouchEvent | MouseEvent) => void
  /** 触摸移动回调 */
  onTouchMove?: (event: TouchEvent | MouseEvent) => void
  /** 触摸结束回调 */
  onTouchEnd?: (event: TouchEvent | MouseEvent) => void
}

选项

选项类型默认值说明
swipeThresholdnumber30滑动最小距离(像素)
swipeTimeoutnumber500滑动最大时间(毫秒)
pinchThresholdnumber0.1双指缩放最小变化
tapTimeoutnumber250点击最大时间(毫秒)
tapThresholdnumber10点击最大移动距离
longPressTimeoutnumber500长按超时时间(毫秒)
enableSwipebooleantrue启用滑动检测
enablePinchbooleantrue启用双指缩放检测
enableRotatebooleantrue启用旋转检测
enableTapbooleantrue启用点击检测
enableLongPressbooleantrue启用长按检测
enableMousebooleantrue启用桌面端鼠标模拟

Composable 用法

你也可以使用 useTouch composable 来实现相同功能:

vue
<script setup>
import { ref, onMounted } from 'vue'
import { useTouch } from 'directix'

const containerRef = ref(null)
const { gesture, bind } = useTouch({
  onSwipeLeft: () => nextSlide(),
  onSwipeRight: () => prevSlide(),
  onTap: (e) => console.log('点击', e.center)
})

onMounted(() => bind(containerRef.value))
</script>

<template>
  <div ref="containerRef">
    滑动我!当前手势: {{ gesture }}
  </div>
</template>

API

typescript
type TouchGesture = 'swipe' | 'pinch' | 'rotate' | 'tap' | 'longPress'

interface TouchGestureEvent {
  type: TouchGesture
  direction?: 'left' | 'right' | 'up' | 'down'
  distance?: number
  angle?: number
  scale?: number
  rotation?: number
  center?: { x: number, y: number }
  event: TouchEvent
}

interface UseTouchOptions {
  /** 滑动手势回调 */
  onSwipe?: (event: TouchGestureEvent) => void
  /** 向左滑动回调 */
  onSwipeLeft?: (event: TouchGestureEvent) => void
  /** 向右滑动回调 */
  onSwipeRight?: (event: TouchGestureEvent) => void
  /** 向上滑动回调 */
  onSwipeUp?: (event: TouchGestureEvent) => void
  /** 向下滑动回调 */
  onSwipeDown?: (event: TouchGestureEvent) => void
  /** 双指缩放回调 */
  onPinch?: (event: TouchGestureEvent) => void
  /** 旋转手势回调 */
  onRotate?: (event: TouchGestureEvent) => void
  /** 点击回调 */
  onTap?: (event: TouchGestureEvent) => void
  /** 长按回调 */
  onLongPress?: (event: TouchGestureEvent) => void
  /** 滑动阈值距离(像素) @default 30 */
  swipeThreshold?: number
  /** 长按持续时间(毫秒) @default 500 */
  longPressDuration?: number
  /** 点击最大持续时间(毫秒) @default 250 */
  tapDuration?: number
  /** 是否禁用 @default false */
  disabled?: boolean | Ref<boolean>
}

interface UseTouchReturn {
  /** 当前正在进行的手势 */
  gesture: Readonly<Ref<TouchGesture | null>>
  /** 绑定触摸事件到元素 */
  bind: (element: HTMLElement) => () => void
}

示例

图片轮播

vue
<template>
  <div v-touch="{ onSwipeLeft: nextSlide, onSwipeRight: prevSlide }" class="carousel">
    <img :src="slides[currentSlide]" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const slides = ['/slide1.jpg', '/slide2.jpg', '/slide3.jpg']
const currentSlide = ref(0)

function nextSlide() {
  currentSlide.value = (currentSlide.value + 1) % slides.length
}

function prevSlide() {
  currentSlide.value = (currentSlide.value - 1 + slides.length) % slides.length
}
</script>

下拉刷新

vue
<template>
  <div
    v-touch="{
      onSwipeDown: refresh,
      swipeThreshold: 100
    }"
    :class="{ refreshing: isRefreshing }"
  >
    下拉刷新
  </div>
</template>

<script setup>
import { ref } from 'vue'

const isRefreshing = ref(false)

async function refresh() {
  isRefreshing.value = true
  await fetchData()
  isRefreshing.value = false
}
</script>

基于 MIT 许可发布