拖拽

位置信息

  • clientX / clientY 获取距离可视区域的距离;
  • pageX / pageY 获取距离文档的(没有滚动条的时候与client一样);
  • screenX / screenY 获取距离屏幕 (指的电脑屏幕);

拖拽涉及到的事件

  • 按下:onmousedown
  • 移动:onmousemove
  • 抬起:onmouseup

拖拽的实现

  1. 找到一个固定值(不变的点)=>鼠标距离拖拽元素的距离(因为按下拖动的时候这个距离不变)。

不变的坐标

  1. 获取拖拽后元素距离body的距离 (offset不行)

拖拽

第一版代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style>
* {
padding: 0;
margin: 0;
}

.box {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
left: 0;
top: 0;
}
</style>
1
<div class="box"></div>
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
/*
拖拽一共三个状态 :
1.按下:onmousedown
2.移动:onmousemove
3.抬起:onmouseup
*/
// 找到一个固定值(不变的点)=>鼠标距离拖拽元素的距离(因为按下拖动的时候这个距离不变)
const box = document.getElementsByClassName("box")[0];
// 1.鼠标按下
box.onmousedown = function (ev) {
// 事件对象的兼容性处理
ev = ev || window.event;
// 获取鼠标距离拖拽元素的距离
const x = ev.clientX - box.offsetLeft;
const y = ev.clientY - box.offsetTop;

// 2.鼠标移动事件 肯定在鼠标按下事件里面
box.onmousemove = function (ev) {
// 事件对象的兼容性处理
ev = ev || window.event;
// 确定box的位置
const left = ev.clientX - x;
const top = ev.clientY - y;
// 设置(得设置绝对定位)
box.style.left = left + "px";
box.style.top = top + "px";
}

// 3.鼠标抬起
box.onmouseup = function () {
// 移除鼠标的移动事件; 因为抬起了鼠标还可以移动
box.onmousemove = null;
box.onmouseup = null;
}

}

bug:鼠标移动过快,鼠标可能会离开div(box),进而到文档上。

第二版代码

将鼠标移动和抬起的对象设置为了整个屏幕,别的未修改。

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
/*
拖拽一共三个状态 :
1.按下:onmousedown
2.移动:onmousemove
3.抬起:onmouseup
*/
// 找到一个固定值(不变的点)=>鼠标距离拖拽元素的距离(因为按下拖动的时候这个距离不变)
const box = document.getElementsByClassName("box")[0];
// 1.鼠标按下
box.onmousedown = function (ev) {
// 事件对象的兼容性处理
ev = ev || window.event;
// 获取鼠标距离拖拽元素的距离
const x = ev.clientX - box.offsetLeft;
const y = ev.clientY - box.offsetTop;

// 2.鼠标移动事件 肯定在鼠标按下事件里面
window.onmousemove = function (ev) {
// 事件对象的兼容性处理
ev = ev || window.event;
// 确定box的位置
const left = ev.clientX - x;
const top = ev.clientY - y;
// 设置(得设置绝对定位)
box.style.left = left + "px";
box.style.top = top + "px";
}

// 3.鼠标抬起
window.onmouseup = function () {
// 移除鼠标的移动事件; 因为抬起了鼠标还可以移动
window.onmousemove = null;
window.onmouseup = null;
}

}

如上代码也可以使用getBoundingClientRect()获取dom位置

1
2
3
4
5
6
7
// 事件对象的兼容性处理
ev = ev || window.event;
// 获取元素的坐标
const boxLocation = box.getBoundingClientRect();
// 获取鼠标距离拖拽元素的距离
const x = ev.clientX - boxLocation.left;
const y = ev.clientY - boxLocation.top;

bug:能超出屏幕范围。

第三版代码

限制边界,别的未修改。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/*
拖拽一共三个状态 :
1.按下:onmousedown
2.移动:onmousemove
3.抬起:onmouseup
*/
// 找到一个固定值(不变的点)=>鼠标距离拖拽元素的距离(因为按下拖动的时候这个距离不变)
const box = document.getElementsByClassName("box")[0];
// 1.鼠标按下
box.onmousedown = function (ev) {
// 事件对象的兼容性处理
ev = ev || window.event;
// 获取元素的坐标
const boxLocation = box.getBoundingClientRect();
// 获取鼠标距离拖拽元素的距离
const x = ev.clientX - boxLocation.left;
const y = ev.clientY - boxLocation.top;

// 边界限制(获取maxLeft,maxTop;视图宽/高-元素宽/高);
const minLeft = 0
const minTop = 0;
const maxLeft = document.documentElement.clientWidth-box.offsetWidth;
const maxTop = document.documentElement.clientHeight-box.offsetHeight;


// 2.鼠标移动事件 肯定在鼠标按下事件里面
window.onmousemove = function (ev) {
// 事件对象的兼容性处理
ev = ev || window.event;
// 确定box的位置
let left = ev.clientX - x;
let top = ev.clientY - y;
// 边界限制
if(left<minLeft){
left=minLeft
}else if(left>maxLeft){
left=maxLeft
};

if(top<minTop){
top=minTop
}else if(top>maxTop){
top=maxTop
};


// 设置(得设置绝对定位)
box.style.left = left + "px";
box.style.top = top + "px";
}

// 3.鼠标抬起
window.onmouseup = function () {
// 移除鼠标的移动事件; 因为抬起了鼠标还可以移动
window.onmousemove = null;
window.onmouseup = null;
}

}

bug:当div里面有图片或者文字的时候,会有默认行为。

最终代码

在鼠标按下里增加了阻止默认行为。

1
2
 // 阻止默认行为
ev.preventDafault?ev.preventDafault():ev.returnValue=false;

拖拽自动回放

设置一个数组用来存放坐标,每次通过pop()将末尾的值赋值给拖拽元素。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
 /*
拖拽一共三个状态 :
1.按下:onmousedown
2.移动:onmousemove
3.抬起:onmouseup
*/
// 找到一个固定值(不变的点)=>鼠标距离拖拽元素的距离(因为按下拖动的时候这个距离不变)
var box = document.getElementsByClassName("box")[0];
var btn = document.getElementsByClassName("btn")[0];

// 设置一个数组存数值;
var stepArr = []

// 1.鼠标按下
box.onmousedown = function (ev) {
// 事件对象的兼容性处理
ev = ev || window.event;
//存放初始值
stepArr.push({
left: parseFloat(window.getComputedStyle(box).left),
top: parseFloat(window.getComputedStyle(box).top)
})
// 阻止默认行为
ev.preventDafault?ev.preventDafault():ev.returnValue=false;
// 获取鼠标距离拖拽元素的距离
var x = ev.clientX - box.offsetLeft;
var y = ev.clientY - box.offsetTop;

// 2.鼠标移动事件 肯定在鼠标按下事件里面
document.onmousemove = function (ev) {
// 事件对象的兼容性处理
ev = ev || window.event;
// 确定box的位置
var left = ev.clientX - x;
var top = ev.clientY - y;
// 边界判断
var minLeft = 0,
maxLeft = document.documentElement.clientWidth - box.clientWidth;
var minTop = 0,
maxTop = document.documentElement.clientHeight - box.clientHeight;
if (left <= minLeft) {
left = minLeft;
} else if (left >= maxLeft) {
left = maxLeft;
}
if (top <= minTop) {
top = minTop;
} else if (top >= maxTop) {
top = maxTop;
}
// 设置(得设置绝对定位)
box.style.left = left + "px";
box.style.top = top + "px";
// 添加移动的坐标
stepArr.push({ "left": left }, { "top": top })
}
}

// 3.鼠标抬起(可以在鼠标按下里面,也可以不在里面)
document.onmouseup = function () {
// 移除鼠标的移动事件; 因为抬起了鼠标还可以移动
document.onmousemove = null;
}

btn.onclick = function () {
// 防止多次点击,先清除定时器
window.clearInterval(this.timer);
var that = this;
// 设置定时器(自定义属性)
this.timer = window.setInterval(function () {
// 拿取末尾的值;原来的数组会改变;
var curStep = stepArr.pop();
if (stepArr.length <= 0) {
box.style.left = curStep.left + "px";
box.style.top = curStep.top + "px";
// 清除定时器
window.clearInterval(that.timer);
} else {
box.style.left = curStep.left + "px";
box.style.top = curStep.top + "px";
}
}, 10)
}