js模拟拖拽

先看看实现效果图, 模拟拖拽最终效果和在桌面上移动文件夹的效果类似

原理介绍

鼠标按下时,拖拽开始。鼠标移动时,被拖拽元素跟着鼠标一起移动。鼠标抬起时,拖拽结束

所以,拖拽的重点是确定被拖拽元素是如何移动的

假设,鼠标按下时,鼠标对象的clientX和clientY分别为x1和x2。元素距离视口左上角x轴和y轴分别为x0和y0

鼠标移动的某一时刻,clientX和clientY分别为x2和y2

所以,元素移动的x轴和y轴距离分别为x2-x1和y2-y1

元素移动后,元素距离视口左上角x轴和y轴的位置分别为

?
1
2
X = x0 + (x2-x1)
Y = y0 + (y2-y1)

代码实现

  将上面的原理用代码实现如下

  鼠标按下时,初始态的x0和y0分别用offsetLeftoffsetTop表示

  鼠标移动时,瞬时态的x和y分别赋值为定位后元素的left和top

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
< div id = "test" style = "height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;" ></ div >
< script >
test.onmousedown = function(e){
  e = e || event;
  //获取元素距离定位父级的x轴及y轴距离
  var x0 = this.offsetLeft;
  var y0 = this.offsetTop;
  //获取此时鼠标距离视口左上角的x轴及y轴距离
  var x1 = e.clientX;
  var y1 = e.clientY;
 
  test.onmousemove = function(e){
   e = e || event;
   //获取此时鼠标距离视口左上角的x轴及y轴距离
   x2 = e.clientX;
   y2 = e.clientY;
   //计算此时元素应该距离视口左上角的x轴及y轴距离
   var X = x0 + (x2 - x1);
   var Y = y0 + (y2 - y1);
   //将X和Y的值赋给left和top,使元素移动到相应位置
   test.style.left = X + 'px';
   test.style.top = Y + 'px';
  }
 
  test.onmouseup = function(e){
   //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
   test.onmousemove = null;
  }
}
</ script >

代码优化

  使用上面的代码时,会出现一个问题。当鼠标拖动的太快,比onmousemove事件的触发间隔还要快时,鼠标就会从元素上离开。这样就停止了元素的拖拽过程

  此时,如果把mousemovemouseup事件都加在document上时,即可解决

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
< div id = "test" style = "height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;" ></ div >
< script >
test.onmousedown = function(e){
  e = e || event;
  //获取元素距离定位父级的x轴及y轴距离
  var x0 = this.offsetLeft;
  var y0 = this.offsetTop;
  //获取此时鼠标距离视口左上角的x轴及y轴距离
  var x1 = e.clientX;
  var y1 = e.clientY;
 
  document.onmousemove = function(e){
   e = e || event;
   //获取此时鼠标距离视口左上角的x轴及y轴距离
   x2 = e.clientX;
   y2 = e.clientY;
   //计算此时元素应该距离视口左上角的x轴及y轴距离
   var X = x0 + (x2 - x1);
   var Y = y0 + (y2 - y1);
   //将X和Y的值赋给left和top,使元素移动到相应位置
   test.style.left = X + 'px';
   test.style.top = Y + 'px';
  }
 
  document.onmouseup = function(e){
   //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
   document.onmousemove = null;
  }
}
</ script >

拖拽冲突

  由于文字和图片默认支持原生拖放,如果将原生拖放和模拟拖拽掺杂在一起,将造成与预想效果不符的情况

  如果拖放的元素内容存在文字,且文字被选中会触发文字的原生拖放效果

  在文字上面双击鼠标,即可选中文字,再移动鼠标时,会触发文字的原生拖放效果,如下所示

  只要在onmousedown事件阻止浏览器的默认行为即可

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
< div id = "test" style = "height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;" >测试文字</ div >
< script >
test.onmousedown = function(e){
  e = e || event;
  //获取元素距离定位父级的x轴及y轴距离
  var x0 = this.offsetLeft;
  var y0 = this.offsetTop;
  //获取此时鼠标距离视口左上角的x轴及y轴距离
  var x1 = e.clientX;
  var y1 = e.clientY;
 
  document.onmousemove = function(e){
   e = e || event;
   //获取此时鼠标距离视口左上角的x轴及y轴距离
   x2 = e.clientX;
   y2 = e.clientY;
   //计算此时元素应该距离视口左上角的x轴及y轴距离
   var X = x0 + (x2 - x1);
   var Y = y0 + (y2 - y1);
   //将X和Y的值赋给left和top,使元素移动到相应位置
   test.style.left = X + 'px';
   test.style.top = Y + 'px';
  }
 
  document.onmouseup = function(e){
   //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
   document.onmousemove = null;
  }
  //阻止默认行为
  return false;
}
</ script >

IE兼容

  以上代码在IE8-浏览器中仍然无法阻止默认行为。此时,为了实现IE兼容,需要使用全局捕获setCapture()和释放捕获releaseCapture()

  首先,先看一下全局捕获的效果

  下面代码中,开启全局捕获之后,页面中的所有点击效果,都相当于针对按钮一的点击效果。释放捕获后,效果消失

  [注意]IE浏览器完全支持全局捕获;chrome不支持,使用全局捕获会报错;firefox不报错,但静默失败

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
< button id = "btn1" >按钮一</ button >
< button id = "btn2" >开启按钮一的全局捕获</ button >
< script >
btn1.onclick = function(){
  alert(1);
}
btn2.onclick = function(){
  if(btn1.setCapture){
   if(btn2.innerHTML.charAt(0) == '开'){
    btn1.setCapture();
    btn2.innerHTML = '关闭按钮一的全局捕获';
   }else{
    btn1.releaseCapture();
    btn2.innerHTML = '开启按钮一的全局捕获';
   }
  }
}
</ script >

  通过在IE浏览器设置全局捕获来达到取消文字原生拖放的默认行为

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
< div id = "test" style = "height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;" >测试文字</ div >
< script >
test.onmousedown = function(e){
  e = e || event;
  //获取元素距离定位父级的x轴及y轴距离
  var x0 = this.offsetLeft;
  var y0 = this.offsetTop;
  //获取此时鼠标距离视口左上角的x轴及y轴距离
  var x1 = e.clientX;
  var y1 = e.clientY;
 
  document.onmousemove = function(e){
   e = e || event;
   //获取此时鼠标距离视口左上角的x轴及y轴距离
   x2 = e.clientX;
   y2 = e.clientY;
   //计算此时元素应该距离视口左上角的x轴及y轴距离
   var X = x0 + (x2 - x1);
   var Y = y0 + (y2 - y1);
   //将X和Y的值赋给left和top,使元素移动到相应位置
   test.style.left = X + 'px';
   test.style.top = Y + 'px';
  }
 
  document.onmouseup = function(e){
   //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
   document.onmousemove = null;
   //释放全局捕获
   if(test.releaseCapture){
    test.releaseCapture();
   }
  }
  //阻止默认行为
  return false;
  //IE8-浏览器阻止默认行为
  if(test.setCapture){
   test.setCapture();
  }
}
</ script >

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值