mouseup事件偶发未触发问题求助(附完整实现代码)
解决mouseup事件偶发不触发的问题
问题根源分析
- 事件绑定对象不一致:mousedown/mouseup绑定在
window,mousemove绑定在document,不同宿主的事件捕获机制差异可能导致事件偶发丢失。 - 页面外mouseup未被捕获:当鼠标拖动过程中移出浏览器窗口后松开,
window的mouseup事件可能无法触发,导致isMouseDown状态一直为true,拖动逻辑不会停止。 - 未阻止默认行为:mousedown时可能触发文本选中、元素拖拽等浏览器默认操作,干扰正常事件流。
- 变量未初始化:
x_point和y_point首次mouseup时可能未定义,导致last_x_point/last_y_point变为undefined,后续旋转计算出错。 - 旋转逻辑错误:原代码中rotateY的计算误用了
last_y_point + x_point,不符合X/Y轴旋转的逻辑,应该使用last_x_point + x_point。
修复后的完整代码
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title></title> <style> * { padding: 0; margin: 0; } body { font-family: "Malgun Gothic", dotum, sans-serif; padding: 30px; } li { list-style-type: none; } .wrapper { margin: 0 auto; width: 300px; height: 600px; box-shadow: 0px 0px 3px 0px rgba(25, 142, 177, 0.75); border-radius: 20px; padding: 20px; display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; justify-items: center; align-items: center; } .item { width: 50px; height: 50px; box-shadow: 0px 0px 3px 0px rgba(188, 28, 105, 0.75); border-radius: 10px; } .item:hover { cursor: pointer; } .item:active { background-color: aliceblue; } .score { border-radius: 10px; margin: 20px auto; line-height: 50px; text-align: center; } .div_x, .div_y { display: inline-block; line-height: 30px; width: 60px; height: 30px; box-shadow: 0px 0px 3px 0px rgba(188, 28, 105, 0.75); border-radius: 5px; margin: 0 10px; } .div_reset { display: block; line-height: 30px; width: 120px; height: 35px; box-shadow: 0px 0px 3px 0px rgba(188, 28, 105, 0.75); border-radius: 5px; margin: 0 auto; text-align: center; font-size: 20px; padding: 0; } .div_reset:hover { background-color: rgb(221, 243, 243); cursor: pointer; } .div_reset:active { background-color: aqua; } </style> </head> <body> <script> let wrapperDiv = document.createElement("div"); wrapperDiv.classList.add("wrapper"); document.querySelector("body").appendChild(wrapperDiv); for (let i = 0; i < 20; i++) { let div = document.createElement("div"); div.classList.add("item"); document.querySelector(".wrapper").appendChild(div); } let div = document.createElement("div"); div.classList.add("score"); document.querySelector("body").appendChild(div); div.append("x : "); let div_x = document.createElement("div"); div_x.classList.add("div_x"); div.appendChild(div_x); div.append("y : "); let div_y = document.createElement("div"); div_y.classList.add("div_y"); div.appendChild(div_y); let div_reset = document.createElement("div"); div_reset.classList.add("div_reset"); document.querySelector("body").appendChild(div_reset); div_reset.innerText = "reset"; let isMouseDown = false; let init_x, init_y; let last_x_point = 0, last_y_point = 0; // 初始化x_point和y_point为0 let x_point = 0, y_point = 0; // 统一绑定到document document.addEventListener("mousedown", function (e) { console.log("mouse down"); // 阻止默认行为,避免文本选中干扰 e.preventDefault(); isMouseDown = true; init_x = e.clientX; init_y = e.clientY; }); document.addEventListener("mouseup", function () { console.log("mouse up"); isMouseDown = false; last_x_point = x_point; last_y_point = y_point; }); // 添加mouseleave事件,防止鼠标移出页面后状态卡住 document.addEventListener("mouseleave", function () { console.log("mouse leave"); isMouseDown = false; last_x_point = x_point; last_y_point = y_point; }); document.addEventListener("mousemove", function (e) { if (isMouseDown) { x_point = e.clientX - init_x; y_point = -e.clientY + init_y; // 修正rotateY的计算逻辑,使用last_x_point + x_point wrapperDiv.style.transform = `rotateX(${(last_y_point + y_point) / 3}deg) rotateY(${(last_x_point + x_point) / 3}deg)`; div_x.innerText = ((last_y_point + y_point) / 3).toFixed(2); div_y.innerText = ((last_x_point + x_point) / 3).toFixed(2); } }); div_reset.addEventListener("click", function () { last_x_point = 0; last_y_point = 0; x_point = 0; y_point = 0; wrapperDiv.style.transform = `rotateX(0deg) rotateY(0deg)`; div_x.innerText = "0.00"; div_y.innerText = "0.00"; }); </script> </body> </html>
关键改动说明
- 统一事件宿主:所有鼠标事件绑定到
document,确保事件捕获的一致性,避免跨对象的事件丢失。 - 补充mouseleave监听:当鼠标离开文档区域时强制重置拖动状态,解决页面外mouseup无法触发的问题。
- 阻止默认行为:mousedown时调用
e.preventDefault(),避免文本选中、默认拖拽等操作干扰事件流。 - 初始化变量:给
x_point和y_point设置初始值0,避免首次mouseup时出现undefined导致的逻辑异常。 - 修正旋转逻辑:将rotateY的计算从
last_y_point + x_point改为last_x_point + x_point,符合X/Y轴旋转的预期逻辑,避免旋转方向错乱。
内容的提问来源于stack exchange,提问作者DevJWP
相关产品推荐
相关产品推荐

