5

快速拖动元素失效问题原因及解决方法

情景再现

晚上在写毕设时,碰到个快速拖动元素,元素会跟不上的问题。具体情形如下:

代码及演示结果

  • 出现的问题

    clipboard.png

  • 绑定在 documentbody

    clipboard.png

  • 绑定在 div 自己身上

    clipboard.png

解决方案

  • 首先,自己的思路是 div 之前已经绑定 mousedown 事件,再绑定 mousemove 事件,绑定事件过多从而出现快速移动失效问题。但这思路自己都感觉是错的,于是网上搜索相关问题,从一篇文章中找到了其中的原因,原因如下:

    鼠标滑动地太快,自然会造成 mousemove 事件多次发生,相应的,事件处理函数也多次被调用,自然造成延迟。延迟之后,元素移动的速度赶不上鼠标移动的速度,可能造成鼠标移出元素的状态,从而触发了 mouseout 事件,从而造成了被拖动元素停止移动。 ——原文来自 鸢飞鱼跃

绑定到bodydocument 之间的细微差别

上面讲到,绑定 mousemove 事件到 bodydocument 上,都能拖动地很流畅,但是它们之间还是有些细微差别的,如下图所示:

  • 绑定到 document
    clipboard.png
  • 绑定到 body
    clipboard.png

Chrome 测试结果中可以看到,绑定到 document 上时,鼠标移动到菜单栏上,元素依旧能被拖动,而绑定在 body 上却做不到这一点。

代码

最后,以下是本次测试中用到的代码,感兴趣的小伙伴可以贴下来自己跑一跑

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <style>
        *{
            padding:0;
            margin:0;
        }
        div{
            position:absolute;
            left:100px;
            top:50px;
            width:100px;
            height:100px;
            border:1px solid #000;
        }
        body{
            width:100vw;
            height:100vh;
        }
    </style>
</head>
<body>

    <div>Drag Me</div>

    <script>
        const div = document.querySelector('div');
        const body = document.body;

        div.addEventListener('mousedown',(event) => {

            let startX = event.clientX,
            startY = event.clientY,
            left = div.offsetLeft,
            top = div.offsetTop;

            let callback = (event) => {
                const stepX = event.clientX - startX,
                stepY = event.clientY - startY;
                div.style.left = `${left + stepX}px`;
                div.style.top = `${top + stepY}px`;
            };

            document.addEventListener('mousemove',callback);

            div.addEventListener('mouseup',() => {
                document.removeEventListener('mousemove',callback);
            });

        });        
    </script>

</body>
</html>

晴天
31 声望1 粉丝