chrome 下鼠标点击BUG!!!! 涉及 mousedown, mousemove, mouseup
事情缘由,正在做echarts的关系图,想要实现节点拖动,但echarts自带的事件无法满足拖动效果,顾想到原生js的addEventListener和removeEventListener,来自己写一个拖动效果出来,可是!!!!!
万恶的chrome有bug、、、
拖动的写法:对元素添加mousedown mouseup,然后在down回调函数里加入mousemove,在up回调函数里移除mousemove,这样就可以实现拖动元素的效果。
代码复现:
var canvas = document.getElementsByTagName('canvas')[0]
canvas.addEventListener('mousedown', function (e) {
console.log('mousedown');
canvas.addEventListener('mousemove', function (e) {
console.log('mousemove');
})
})
canvas.addEventListener('mouseup', function (e) {
console.log('mouseup');
canvas.removeEventListener('mousemove', function (e) {
console.log('remove mousemove');
})
console.log(canvas);
})
bug现场图

至于这个问题怎么解决:
我搜了半天...
最终找到这个帖子,说的很详尽👇
// 在Chrome下有一个关于mousemove的bug是:
// 1.在触发mouseup事件时,包括在触发click和contextmenu时,也会触发mousemove事件;
// 2.更诡异的是,当你连续的触发contextmenu事件时,mousedown事件会被mousemove代替,
// 目前我想到的一个解决方案,通过时间戳比较:
var body = document.querySelector("h1");
var timeStamp;
body.addEventListener("mousedown", function (e) {
console.log("mouse down");
}, false);
body.addEventListener("mouseup", function (e) {
console.log("mouse up");
timeStamp = e.timeStamp;
})
body.addEventListener("mousemove", function (e) {
if (!timeStamp || ( e.timeStamp - timeStamp > 10)) {
console.log("mouse move");
}
})
// 这样能避免在触发click事件,或者触发鼠标左键mouseup引起的mousemove,但是对于上面描述的contextmenu引起的mousemove还是不能很好解决
最终我的代码实现
var move = {
offsetX: 0,
offsetY: 0
}
var canvas = document.querySelector('canvas')
canvas.addEventListener('mousedown', function (e) {
// console.log('mousedown', e);
move.offsetX = e.offsetX
move.offsetY = e.offsetY
}, false)
canvas.addEventListener('mouseup', function (e) {
console.log('mouseup', e);
move.offsetX = 0
move.offsetY = 0
})
canvas.addEventListener('mousemove', debounce(function (e) {
// console.log(e.offsetX, move.offsetX);
var offsetX = e.offsetX - move.offsetX
var offsetY = e.offsetY - move.offsetY
console.log('xChange: ' + offsetX, 'yChange: ' + offsetY);
}, 12, true))
/**
* 防反跳。fn函数在最后一次调用时刻的delay毫秒之后执行!
* @param fn 执行函数
* @param delay 时间间隔
* @param isImmediate 为true,debounce会在delay时间间隔的开始时立即调用这个函数
* @returns {Function}
*/
function debounce(fn, delay, isImmediate) {
var timer = null; //初始化timer,作为计时清除依据
return function () {
var context = this; //获取函数所在作用域this
var args = arguments; //取得传入参数
clearTimeout(timer);
if (isImmediate && timer === null) {
//时间间隔外立即执行
fn.apply(context, args);
timer = 0;
return;
}
timer = setTimeout(function () {
fn.apply(context, args);
timer = null;
}, delay);
}
}
有什么不同见解可以在评论区共同讨论

浙公网安备 33010602011771号