You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3中如何阻止拖拽按钮时触发@click事件?

问题

我制作了可拖拽按钮,用户可在屏幕任意位置拖动它,点击按钮时会弹出通知。但存在问题:拖拽按钮的操作会被判定为@click事件,导致通知自动弹出,我希望阻止该场景下的点击事件。

原代码:

<script setup>
import { ref } from 'vue'
import IconSoundFilled from '~icons/ant-design/sound-filled'
import { useDraggable } from '@vueuse/core'
import PageSubtitle from '../../common/components/Atom/PageSubtitle.vue'
import { useTimestamp } from '@vueuse/core'

const visible = ref<boolean>(false)
const el = ref<HTMLElement | null>(null)

const { x, y, style } = useDraggable(el, {
  initialValue: { x: 500, y: 60 }
})

const noticeList: object[] = [
  { title: 'yo' },
  { title: 'yo' },
  { title: 'yo' },
  { title: 'yo' },
  { title: 'yo' },
  { title: 'yo' }
]

const timestamp = useTimestamp({ offset: 0 })
const date = new Date(timestamp.value)
</script>

<template>
  <button
    ref="el"
    :style="style"
    class="item bg-gray-dark fixed z-10 h-[54px] w-[54px] rounded-full shadow-lg"
    @click="visible = !visible"
  >
    <button
      class="bg-red absolute top-0.5 right-0.5 h-[15px] w-[15px] rounded-full text-xs font-bold text-white"
    >
      1
    </button>
    <div class="flex justify-center">
      <IconSoundFilled class="hover:text-red text-xl text-white" />
    </div>
  </button>
  <div
    v-if="visible"
    class="bg-white-light border-gray fixed h-96 w-4/6 overflow-auto rounded-md border border-solid px-6 py-2"
  >
    <div class="mb-3 flex justify-center">
      <PageSubtitle text="Notice" class="right-auto" />
    </div>
    <div
      v-for="(item, index) in noticeList.slice().reverse()"
      :key="index"
      class="flex gap-6 text-sm"
    >
      <IconSoundFilled v-if="index == 0" class="text-red text-xl" />
      <IconSoundFilled v-else class="text-xl text-black" />
      {{ item.title }}
      {{ date }}
      <br />
      <br />
    </div>
  </div>
</template>

解决方法

核心思路是利用useDraggable返回的isDragging状态,区分拖拽操作和纯点击操作,仅在非拖拽状态下触发弹窗切换。

修改后的完整代码

<script setup>
import { ref } from 'vue'
import IconSoundFilled from '~icons/ant-design/sound-filled'
import { useDraggable } from '@vueuse/core'
import PageSubtitle from '../../common/components/Atom/PageSubtitle.vue'
import { useTimestamp } from '@vueuse/core'

const visible = ref<boolean>(false)
const el = ref<HTMLElement | null>(null)

// 解构出useDraggable内置的isDragging状态
const { x, y, style, isDragging } = useDraggable(el, {
  initialValue: { x: 500, y: 60 }
})

const noticeList: object[] = [
  { title: 'yo' },
  { title: 'yo' },
  { title: 'yo' },
  { title: 'yo' },
  { title: 'yo' },
  { title: 'yo' }
]

const timestamp = useTimestamp({ offset: 0 })
const date = new Date(timestamp.value)

// 封装点击逻辑,仅在非拖拽状态下切换弹窗
const toggleNotice = () => {
  if (!isDragging.value) {
    visible.value = !visible.value
  }
}
</script>

<template>
  <button
    ref="el"
    :style="style"
    class="item bg-gray-dark fixed z-10 h-[54px] w-[54px] rounded-full shadow-lg"
    @click="toggleNotice"
  >
    <button
      class="bg-red absolute top-0.5 right-0.5 h-[15px] w-[15px] rounded-full text-xs font-bold text-white"
    >
      1
    </button>
    <div class="flex justify-center">
      <IconSoundFilled class="hover:text-red text-xl text-white" />
    </div>
  </button>
  <div
    v-if="visible"
    class="bg-white-light border-gray fixed h-96 w-4/6 overflow-auto rounded-md border border-solid px-6 py-2"
  >
    <div class="mb-3 flex justify-center">
      <PageSubtitle text="Notice" class="right-auto" />
    </div>
    <div
      v-for="(item, index) in noticeList.slice().reverse()"
      :key="index"
      class="flex gap-6 text-sm"
    >
      <IconSoundFilled v-if="index == 0" class="text-red text-xl" />
      <IconSoundFilled v-else class="text-xl text-black" />
      {{ item.title }}
      {{ date }}
      <br />
      <br />
    </div>
  </div>
</template>

关键说明

  • isDragging是useDraggable内置的响应式状态,拖拽过程中自动变为true,拖拽结束后恢复false
  • 通过点击事件中对isDragging.value的判断,精准过滤拖拽操作带来的误触发,仅在纯点击时切换弹窗可见性

内容的提问来源于stack exchange,提问作者BrownEyedSoul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 09:10:22