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代码块。
修复方案
- 给
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] // 后续原逻辑不变 } }
- 移除
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() } // ... 剩余原逻辑 }
- 改用组件响应式状态
尽量把MOUSEX、MOUSEY这类状态放到组件的响应式变量里(比如ref),而不是全局变量,减少不同事件之间的状态干扰。
内容的提问来源于stack exchange,提问作者codeninja
相关产品推荐
相关产品推荐

