前端拖拽的实现

7,706 阅读4分钟

onmousemove 实现拖拽

说到前端的拖拽很多前端开发人员会很烦,尤其是涉及到边缘判断这块,还有就是兼容性。学习前端之初我一直是使用onmousemove来实现拖拽的,下面展示要给传统的通过onmousemove实现的拖拽。 代码如下:

HTML:
<div class="wrap">
    <div class="drag"></div>
</div>
CSS:
.wrap{
  width: 600px;
  height: 600px;
  margin: 150px auto;
  border: 1px solid #000000;
  position: relative;
}
.drag{
  width: 50px;
  height: 50px;
  background: #DB0304;
  cursor: pointer;
  position: absolute;
}
JS:
var dragDom = document.querySelector('.drag');
var wrap = document.querySelector('.wrap');

function drag(ele){
  var oldX, oldY, newX, newY;
  ele.onmousedown = function(e){
      this.style.position = 'relative';//元素相对定位
      if(!this.style.left && !this.style.top){//第一次设置left、top为0
        this.style.left = 0;
        this.style.top = 0;
      }
      oldX = e.clientX;//记录初始光标相对于视窗坐标
      oldY = e.clientY;
      document.onmousemove = function(e){
        newX = e.clientX;//获取当前新光标相对于视窗坐标
        newY = e.clientY;
        ele.style.left = parseInt(ele.style.left) + newX - oldX + 'px';//更新
        ele.style.top = parseInt(ele.style.top) + newY - oldY + 'px';
        oldX = newX;//新坐标变为老坐标
        oldY = newY;
      }
      document.onmouseup = function(){
        document.onmousemove = null;
        document.onmouseup = null;
      }
  }
}

drag(dragDom)

最终效果:

展示效果

代码分析:

首先是给dom绑定一个mousedown事件,并且记录初始化光标相对于浏览器的坐标。里面我们为什么使用 document 呢?因为当我们鼠标快速移动的时候经常会出现鼠标移出了目标dom这个时候如果我们给dom绑定mousemove事件的话会出现卡顿现象。所以我们给document加这个事件,里边我们设置新的x,y坐标是当前的鼠标相对于浏览器的坐标,然后我们我们开始计算此时的delta,也就是改变的值:

deltaX = newX - oldX, deltaY = newY - oldY

所以我们获取当前dom的位置加上变化的量,就是新的位置。并且把新的位置复制给oldX和oldY。

最后我们通过绑定mouseup来解绑事件。OK,大功告成,是不是也很简单,这里是因为我们没有加边缘判断,所以相对简单一些,如果我们要根据实际的项目需求,加上不同的边缘判断就不简单了。

ondragover 实现

这里我得先给大家大概讲讲这块的一个基本知识:

HTML draggable 属性

<div draggable="true></div>

兼容性:

IE Firefox Chrome Safari Opera
9+ true true true true

结论:我们可以大胆的使用了,毕竟兼容IE8以及IE8之前的网页拖拽现在用的比较少了。

在拖动目标上触发事件 (源元素):

  • ondragstart - 用户开始拖动元素时触发
  • ondrag - 元素正在拖动时触发
  • ondragend - 用户完成元素拖动后触发

释放目标时触发的事件:

  • ondragenter - 当被鼠标拖动的对象进入其容器范围内时触发此事件
  • ondragover - 当某被拖动的对象在另一对象容器范围内拖动时触发此事件
  • ondragleave - 当被鼠标拖动的对象离开其容器范围内时触发此事件
  • ondrop - 在一个拖动过程中,释放鼠标键时触发此事件

DataTransfer

在进行拖放操作时,DataTransfer 对象用来保存,通过拖放动作,拖动到浏览器的数据。它可以保存一项或多项数据、一种或者多种数据类型。

这个对象可以从所有拖动事件 drag events 的 dataTransfer 属性上获取,但是不能单独创建。

DOMString dataTransfer.getData(format)

DOMString dataTransfer.setData()

上边是dataTransfer的两个方法,getData(format)中的format方法参数:text/plain, text/uri-list。我们常用的主要是text。format就是DOMString类型。

下面我们就简单的实现一个拖拽。

代码如下:

<!DOCTYPE HTML>
<html>
    <head>
    <meta charset="utf-8">
    <title>Drag</title>
    <style type="text/css">
    	*{
    		margin: 0px;
    		padding:0px;
    	}
    	#con{
    		width:100%;
    		height:500px;
    		border:1px dotted #999;
    	}
    	#img{
    		margin-left: 10px;
    		margin-top: 0px;
    	}
    </style>
    </head>
    <body>
        <div id="con" ondragover="allowDrop(event);" ondrop="drop(event);">
            <img id="img" src="./hot.png" draggable="true" ondragstart="drag(event)"/>
        </div>
    </body>
    <script type="text/javascript">
        //保存位置的状态值
        var pos={
        	parent_top:0,
        	parent_left:0,
        	cur_top:0,
        	cur_left:0
        }
        
        function allowDrop(ev){				//ev是事件对象
        	ev.preventDefault();			//取消事件已经关联的默认活动
        }
        
        function drag(ev){
        	//dataTransfer是一个媒介,将目标对象放入媒介
        	//dataTransfer对象用来保存被拖动的数据,仅在拖动事件有效
        	//这里将被拖动元素的id保存为名为Text的键值对中
        	ev.dataTransfer.setData("Text",ev.target.id);
        	//获取被拖动对象相对于上层元素顶边和左边位置	
        	pos.parent_top=ev.target.offsetTop;
        	pos.parent_left=ev.target.offsetLeft;
        	pos.cur_top=ev.screenY;
        	pos.cur_left=ev.screenX;
        }
        function drop(ev){
        	var new_top,new_left;
        	ev.preventDefault();
            var data=ev.dataTransfer.getData("text");	
        	var elem=document.getElementById(data);
        	elem.style.marginLeft=pos.parent_left+ev.screenX-pos.cur_left+"px";
        	elem.style.marginTop=pos.parent_top+ev.screenY-pos.cur_top+"px";
        }
    </script>
</html>

上边是实现简单的拖拽,工作中如果有这块需求还得根据具体业务需求来加相应的方法。