首页 > 技术文章 > 视二: 原生JS 实现拖拽&临界值

bala 2019-09-02 17:14 原文

  今天来说下原生JS如何实现拖拽的功能 —— 只能在可拖拽范围内进行拖拽。实现该功能的思路,就是: 通过改变拖拽元素的位置(left, top)来实现。

  在这个功能中我们需要考虑三个事件和两个临界点。

  拖拽的三事件:

    • 鼠标落下 ——  mousedown
    • 鼠标移动 ——  onmousemove
    • 鼠标抬起 ——  mouseup

  两个临界点:

    • 最小的临界点
    • 最大的临界点

  

  接下来,我们先写好html代码和css  样式。我们定义一个可拖拽的容器 wrapper  和 一个 拖拽框  box。

// CSS
*
{ margin: 0; padding: 0; } .wrapper{
   position:relative; width: 500px; height: 500px; margin: 150px auto; border: 1px solid #000; } .box{ position: absolute; width: 200px; height: 200px; background-color: red; cursor: move; }
// HTML
<div class="wrapper">
    <div class="box"></div>
</div>

 

  HTML 和 CSS 写好了,接下来我们来写JS。

  首先我们要获得到  wrapper  和  box 这个两个元素。采用 document.getElementsByClassName('className')[0]  。为什么要用 [0] ?  因为  getElementByClassName 获取到的是一个类数组的数据。将document.getElementsByClassName('wrapper')打印出来,如下图:

  

  所以需要获取下标为 0  的第一个元素。
 

  接着,我们实现 监听【鼠标落下】的功能,为了后续的【当鼠标移除可拖拽范围】,此处我们先获取 wrapper 元素。

var wrap= document.getElementByClassName('wrapper')[0],
    oBox = document.getElementByClassName('box')[0]

oBox.addEventListener('mousedown', function(e){
    
     var lastX = e.clientX,
         lastY = e.clientY;

});    

 

【鼠标移动】,此时我们要获取鼠标移动所在的坐标,并计算出此时的坐标与上一个坐标的差值。

解析图如下:鼠标在 a  点位置落下,移动到  b  点,此时需要计算出  移动的垂直和水平距离。

document.onmousemove = function (e){
// 此处 ,为什么要用document ? 而不用 oBox ? 如果用 oBox ,当鼠标快速移动的时候,会出现 移动块(box) 脱落,不会跟着鼠标移动。
// 如果后续设置了鼠标只能在拖拽框中进行拖拽,此处就可以采用 oBox ,但是后面的对应的鼠标移动事件也需要使用 oBox 。
var nowX = e.clientX, nowY = e.clientY var disX = nowX - lastX, disY = nowY - lastY var left = oBox.offsetLeft + disX, // offsetLeft 是 当前元素距离父级元素的距离 top = oBox.offsetTop + disY }

  在css 中,我们给 wrapper 设置了——相对定位,给 box 元素设置了——绝对定位,此时,我们就可以将 上面代码中的  left  和  top  赋值给  box  元素

oBox.style.left = left + 'px' 
oBox.style.top = top + 'px'

*此时,需要注意的是,拖拽的过程中,鼠标是不停的在移动,所以,此时的初始点  (a)  和 移动终点  (b)  也是在不断的改变,所以我们需要重新给  初始点 a  的水平和垂直距离赋值。

lastX = nowX
lastY = nowY

 

【鼠标抬起】:鼠标落下和鼠标移动实现了, 那当我们将 box 元素拖拽到目标位置点的时候,此时,我们就需要实现  鼠标抬起  的功能。代码如下:

document.onmouseup = function(){
    document.onmousemove = null;
}

 

上面实现了拖拽的功能,但是发现 移动块可以满屏的拖,那如果只想要在可拖拽范围内拖拽呢?

此时就需要考虑临界值的问题。即左上角 和右下角。在鼠标移动事件中,我们增加 left  和 top  取值限制。即当 右下角坐标 >  left / top  > 左上角坐标, 代码如下:


 var wrapLeft = wrap.getBoundingClientRect().left
 var wrapLeftLast = wrapLeft + 302
// 最小临界值
left = left <= wrapLeft ? wrapLeft.left : left
top = top <= 150 ? 150 : top;

// 最大临界值
left = left >= wrapLeftLast ? wrapLeftLast : left
top = top >= (500 - 200 + 152) ? 452 : top;

此处,因为我给  box 设置了居中,所以采用了 getBoundingClientRect().left  获取  box  左侧距离窗口的距离。如果需求中没有给box 设置,可以直接将 wrapLeft = 0 ,  wrapLeftLast = (wrapper.width  - box.width) 来代替。

 

最后,我们再实现上文说的后续功能。当鼠标移出拖拽范围 wrapper 的时候,我们停止拖拽行为。代码如下:

wrap.onmouseleave = function(e){
    document.onmousemove = null
}

 

好了,思路都解析完了,下面贴上完整的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .wrapper{
            width: 500px;
            height: 500px;
            margin: 150px auto;
            border: 1px solid #000;
        }
        .box{
            position: absolute;
            width: 200px;
            height: 200px;
            background-color: red;
            cursor: move;
        }
    </style>
</head>
<body>
    <div class="wrapper">
        <div class="box"></div>
    </div>
    <script>
        var oBox = document.getElementsByClassName('box')[0],
           wrap = document.getElementsByClassName('wrapper')[0],
           wrapLeft = wrap.getBoundingClientRect().left,
           wrapLeftLast = wrapLeft +  302;   // 302 :  考虑了边框 两个像素的问题

        oBox.addEventListener('mousedown', function(e){
            var lastX = e.clientX,
               lastY = e.clientY;

            document.onmousemove = function(e){
                var nowX = e.clientX,
                   nowY = e.clientY;

                var disX = nowX - lastX,
                   disY = nowY - lastY;

                var left = oBox.offsetLeft + disX,
                   top = oBox.offsetTop + disY;

                left = left <= wrapLeft ? wrapLeft.left : left;
                top = top <= 150 ? 150 : top;

                left = left >= wrapLeftLast ? wrapLeftLast : left;
                top = top >= (500 - 200 + 152) ? 452 : top;  // 452 考虑了边框两像素的问题

                oBox.style.left = left + 'px';
                oBox.style.top = top + 'px';

                lastX = nowX;
                lastY = nowY;
            }

            document.onmouseup = function(){
                document.onmousemove = null
            }
        })

        wrap.onmouseleave = function(e){
            document.onmousemove = null
        }
    </script>
</body>
</html>

 

推荐阅读