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

Vue.js中Canvas元素mousemove与mousedown事件共存失效问题

Vue中Canvas同时绑定mousemove和mousedown时,mousedown无法触发的问题

我在Vue.js项目里给Canvas元素绑定了mousemove和mousedown事件监听器,同时使用这两个事件时,只有mousemove生效,mousedown触发不了;但单独用mousedown时可以正常触发。请问有没有遗漏的配置或者导致这个问题的原因?

模板代码

<div>
  <canvas
    ref="canvasRef"
    @mousemove="handleHoverListener"
    @mousedown="mousedownListener"
    :width="WIDTH"
    :height="HEIGHT"
  ></canvas>
</div>

脚本代码(TypeScript)

import { IonButtons } from "@ionic/vue"
import { defineComponent, onMounted, ref, watch, PropType } from "vue"
import { GraphTables } from "@/interfaces/GraphTables"
import { Movable } from "@/classes/Movable"
import { Graph } from "@/classes/Graph"
import { Edge } from "@/interfaces/Edge"
import { Variable } from "@/interfaces/Variable"
import { TableVariable } from "@/interfaces/TableVariable"
import GetDataDic from "@/composables/getDataDic"
//import variableModal from "@/components/VariableModal.vue";

let HEIGHT = 0
let WIDTH = 0
let MOUSEX: number
let MOUSEY: number
const DELTA = 10
let LAST_POS = [0, 0]
let ctx: any
const isModalOpen = false
const singleTableData: any = ref<Variable>()
const singleVariable: any = ref<Variable>()
const variableModal = ref<boolean>(false)
const moreInfoModal = ref<boolean>(false)
const tblName = ref<string>("")

export default defineComponent({
  name: "CanvasComp",
  props: {
    jsonData: {
      type: Object as PropType<GraphTables[]>,
      required: true
    },
    allEdges: {
      type: Object as PropType<Edge[]>,
      required: true
    }
  },
  components: {
    IonButtons
  },
  methods: {
    mousedownListener(event: any) {
      MOUSEX = event.x
      MOUSEY = event.y

      const deltaX = Math.abs(MOUSEX - LAST_POS[0])
      const deltaY = Math.abs(MOUSEY - LAST_POS[1])
      if (deltaX >= DELTA || deltaY >= DELTA) {
        LAST_POS = [MOUSEX, MOUSEY]
        for (const e in Movable.graph.edges) {
          Movable.graph.edges[e].resetCurrDefaults()
        }

        for (const i in Movable.graph.tables) {
          //console.log(Movable.graph.tables[i].currPos[0] +" ndi "+ Movable.graph.tables[i].currPos[1]);
          const xDiff = Math.abs(Movable.graph.tables[i].currPos[0] - MOUSEX)
          const yDiff = Math.abs(Movable.graph.tables[i].currPos[1] - MOUSEY)
          // console.log(MOUSEX +" | "+ MOUSEY);
          // console.log("Table: " + Movable.graph.tables[i].name + " xDiff: " + xDiff + ", yDiff: " + yDiff);
          // check if mouse is within bufferzone
          if (
            xDiff < Movable.graph.bufferZone &&
            yDiff < Movable.graph.bufferZone
          ) {
            // check if was hovering in previous state
            // if true, then change nothing and break the loop
            if (Movable.graph.isHovering) {
              break
            }
            Movable.graph.isHovering = true
            //console.log(Movable.graph.tables[i]);
            variableModal.value = true

            //Putting the table data to TableVariable interface for easier access
            const selectedTableData = ref<TableVariable>(
              Movable.graph.tables[i]
            )
            singleTableData.value = selectedTableData.value.var
            tblName.value = Movable.graph.tables[i].name
            //console.log(singleTableData.value.var);

            Movable.graph.tables[i].highlight()
            //console.log(Movable.graph.tables[i]);

            break
          } else {
            Movable.graph.tables[i].resetDefaults()
            Movable.graph.isHovering = false
          }
        }
        Movable.c.clearRect(0, 0, window.innerWidth, window.innerWidth)
        Movable.graph.drawCanvas()
      }
    },

    handleHoverListener(event: any) {
      MOUSEX = event.x
      MOUSEY = event.y

      const deltaX = Math.abs(MOUSEX - LAST_POS[0])
      const deltaY = Math.abs(MOUSEY - LAST_POS[1])
      if (deltaX >= DELTA || deltaY >= DELTA) {
        LAST_POS = [MOUSEX, MOUSEY]
        for (const e in Movable.graph.edges) {
          Movable.graph.edges[e].resetCurrDefaults()
        }

        for (const i in Movable.graph.tables) {
          const xDiff = Math.abs(Movable.graph.tables[i].currPos[0] - MOUSEX)
          const yDiff = Math.abs(Movable.graph.tables[i].currPos[1] - MOUSEY)

          if (
            xDiff < Movable.graph.bufferZone &&
            yDiff < Movable.graph.bufferZone
          ) {
            // check if was hovering in previous state
            // if true, then change nothing and break the loop
            if (Movable.graph.isHovering) {
              break
            }
            Movable.graph.isHovering = true

            Movable.graph.tables[i].highlight()
            //console.log(Movable.graph.tables[i]);

            break
          } else {
            //Movable.graph.tables[i].resetDefaults();
            Movable.graph.isHovering = false
          }
        }
        Movable.c.clearRect(0, 0, window.innerWidth, window.innerWidth)
        Movable.graph.drawCanvas()
      }
    }
  },
  setup(props) {
    const canvasRef = ref()
    HEIGHT = window.innerHeight - 10
    WIDTH = window.innerWidth
    const { tableData, decodeHtmlEntities, getVariableNameById } = GetDataDic()
    tableData()

    const handleClick = (variable: Variable) => {
      moreInfoModal.value = true
      singleVariable.value = variable
    }
    const showVariableModal = (modal: boolean) => {
      variableModal.value = modal
    }

    const showMoreInfoModal = (modal: boolean) => {
      moreInfoModal.value = modal
    }

    watch(
      () => [props.jsonData.length, props.allEdges.length],
      () => {
        //Movable.c = ctx;
        Graph.allEdges = props.allEdges
        Movable.graph = new Graph(props.jsonData)
        //ctx.clearRect(0, 0, WIDTH, HEIGHT);
        //console.log(graph.topic);
        Movable.graph.drawCanvas()
      }
    )

    onMounted(() => {
      Movable.c = canvasRef.value.getContext("2d")
    })

    return {
      canvasRef,
      HEIGHT,
      WIDTH,
      isModalOpen,
      decodeHtmlEntities,
      getVariableNameById,
      showVariableModal,
      showMoreInfoModal,
      confirm,
      variableModal,
      moreInfoModal,
      singleTableData,
      singleVariable,
      tblName,
      handleClick
    }
  }
})

问题原因及修复方案

核心问题:共享全局变量导致mousedown逻辑被跳过

你代码里的LAST_POS、MOUSEX、MOUSEY是全局共享变量,mousemove事件触发频率极高,会快速更新这些变量。当你点击Canvas时,mousedown事件触发前,mousemove已经把LAST_POS更新到了当前鼠标位置,导致mousedown里的deltaX和deltaY都小于DELTA(10),直接跳过了后续核心逻辑。

验证方式

在mousedownListener开头添加打印:

console.log("LAST_POS:", LAST_POS, "当前鼠标位置:", event.x, event.y);

点击时会发现两者几乎一致,deltaX/deltaY为0,触发不了if代码块。

修复方案

  1. 给mousedown单独维护位置变量
    新增独立的位置变量,避免和mousemove共享:
// 新增全局变量
let LAST_MOUSE_DOWN_POS = [0, 0];

// 在mousedownListener里替换变量
mousedownListener(event: any) {
  MOUSEX = event.x
  MOUSEY = event.y

  const deltaX = Math.abs(MOUSEX - LAST_MOUSE_DOWN_POS[0])
  const deltaY = Math.abs(MOUSEY - LAST_MOUSE_DOWN_POS[1])
  if (deltaX >= DELTA || deltaY >= DELTA) {
    LAST_MOUSE_DOWN_POS = [MOUSEX, MOUSEY]
    // 后续原逻辑不变
  }
}
  1. 移除mousedown中的位置差判断
    如果mousedown不需要判断鼠标移动距离,直接去掉那层if,确保点击逻辑一定会执行:
mousedownListener(event: any) {
  MOUSEX = event.x
  MOUSEY = event.y
  // 去掉delta判断,直接执行后续逻辑
  LAST_POS = [MOUSEX, MOUSEY]
  for (const e in Movable.graph.edges) {
    Movable.graph.edges[e].resetCurrDefaults()
  }
  // ... 剩余原逻辑
}
  1. 改用组件响应式状态
    尽量把MOUSEX、MOUSEY这类状态放到组件的响应式变量里(比如ref),而不是全局变量,减少不同事件之间的状态干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 09:45:12