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

mouseup事件偶发未触发问题求助(附完整实现代码)

解决mouseup事件偶发不触发的问题

问题根源分析

  1. 事件绑定对象不一致:mousedown/mouseup绑定在window,mousemove绑定在document,不同宿主的事件捕获机制差异可能导致事件偶发丢失。
  2. 页面外mouseup未被捕获:当鼠标拖动过程中移出浏览器窗口后松开,window的mouseup事件可能无法触发,导致isMouseDown状态一直为true,拖动逻辑不会停止。
  3. 未阻止默认行为:mousedown时可能触发文本选中、元素拖拽等浏览器默认操作,干扰正常事件流。
  4. 变量未初始化:x_point和y_point首次mouseup时可能未定义,导致last_x_point/last_y_point变为undefined,后续旋转计算出错。
  5. 旋转逻辑错误:原代码中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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 21:38:10