Vue自定义长按指令

新建文件longpress.ts文件

// longpress.ts
import { DirectiveBinding } from 'vue'
const longpress = {
  beforeMount(el: any, binding: DirectiveBinding) {
    const cb = binding.value
    const duration = binding.arg || 800  // 长按等待时间

    let timer: number | null = null
    let isLongPress = false  // 记录是否触发长按

    const longPress = (e: TouchEvent) => {
      if (e.type === 'touchstart') {
        timer = window.setTimeout(() => {
          isLongPress = true
          if (typeof cb === 'function') cb(el) // 3. 回调函数
        }, +duration)
        return false
      } else if (e.type === 'touchmove') {
        if (isLongPress) {
          e.preventDefault() // 1. 阻止默认行为
        }
        // 2. 若移动了就不触发长按事件
        if (timer) {
          clearTimeout(timer)
          timer = null
        }
      } else if (e.type === 'touchend') {
        if (isLongPress) {
          e.preventDefault() // 1. 阻止默认行为
          isLongPress = false
        }
        if (timer) {
          clearTimeout(timer)
          timer = null
        }
        return false
      }
    }

    el.addEventListener('touchstart', longPress)
    el.addEventListener('touchmove', longPress)
    el.addEventListener('touchend', longPress)

    el['longPress'] = longPress
  },
  unmounted(el: any) {
    el.removeEventListener('touchstart', el['longPress'])
    el.removeEventListener('touchmove', el['longPress'])
    el.removeEventListener('touchend', el['longPress'])
  },
}

export default longpress

main.ts中引入并全局定义

// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import longpress from './directives/longpress'

const app = createApp(App)
app.directive('longpress ', longpress)

app.mount('#app')

在组件中直接使用

// demo.vue
<template>
	<div v-for="item in msgs" :key="item.id">
        <img class="avatar" :src="item.icon" :data-id="item.id" v-longpress="onPress"/>
        <div class="name">{{ item.name }}</div>
    </div>
</template>

<script setup>
	import { ref } from "vue"
	const msgs = ref([
	  {"id":1,"name":"朱鸢","icon":"https://bbs-static.miyoushe.com/static/2024/03/18/d70f25b443df8029e05cd20a897126a6_7820117047572824878.png"},
	  {"id":2,"name":"艾莲","icon":"https://bbs-static.miyoushe.com/static/2023/11/03/060c8ba17ff372078dcf62d4d8b46d28_7229548510559384850.png"},
	  {"id":3,"name":"丽娜","icon":"https://bbs-static.miyoushe.com/static/2023/11/03/6ce65ace05f501e8f6af3616dea1c634_1107629373726729394.png"}
	])
	const onPress = (e) => {
		console.log("press", e, e.dataset.id)
	}
</script>

这个指令里有三点处理需要特别说明一下(对应代码注释里的序号):

  1. 浏览器中长按会有一些默认的行为(比如弹出菜单栏),那么就需要阻止掉这些行为的触发。在touchend并且判断是长按的情况下才阻止,防止在非长按时默认行为(比如滑动)也被阻止。在touchmove中也阻止,防止在长按后不松手的情况下滑动导致以下报错。
    在这里插入图片描述
  2. 若在长按触发前触发了移动事件,则清除长按监听。因为这个时候用户很可能只是想滑动页面,不需要触发长按事件。
  3. 长按触发后调用回调函数,将当前长按的目标元素传回。这个有很大作用,比如本文的示例,头像列表由数据循环渲染而成,每个头像都添加了长按事件,那么当前长按的是哪个,就需要有参数的回调了。由于在指令中无法通过以下形式传参,
    <div v-for="item in msgs" :key="item.id">
      <img :src="item.icon" v-longpress="onPress(item.id)"/> // 错误的传参方式 ❌
    </div>
    
    因此,我们在作用元素上添加自定义属性data-id,将id设置在此属性上。
    <div v-for="item in msgs" :key="item.id">
      <img :src="item.icon" :data-id="item.id" v-longpress="onPress"/> // 将参数设置在自定义属性上
    </div>
    
    当我们触发长按时,长按指令会回调当前元素,此时只需要通过e.dataset.id就可以获取到长按项对应的id了。如下图所示:
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值