lisa的个人博客

慢慢理解世界,慢慢更新自己

0%

实现一个对话框支持拖拽

面试中经常会被问到有没有做过类似模态框的拖拽行为,当时脑子里面没有一个具体的实现方向,大致思路,其实自己静下心动手实践一下也不难,了解如何去操作,实现逻辑屡清楚了基本离成功就不远了,需要注意的事,这里面涉及到很多细节问题,比如获取鼠标点击位置距离wrap的一个相对位置,wrap自身的一个宽度获取,有滚动条的时候,对于滚动距离的获取等等,这些一定要弄清楚了。

创建一个dialog类,实现一个dialog对话框,可拖拽

效果图

1.png

2.png

3.png

具体实现如下:

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
class Dialog {
constructor(text) {
this.x = 0;
this.y = 0;
this.isMoving = false;
this.dialog = null;
this.box = null;
this.text = text || '';
this.toLeft = 0;
this.toTop = 0;
this.a = 1;
}

open() {
this.dialog = document.createElement('div');
this.dialog.style = `
width:200px;
height:100px;
padding:20px;
background-color:#ccc;
position:absolute;
top:50%;
left:50%;
border-radius:4px;`;
this.dialog.innerText = this.text;
this.dialog.addEventListener('click', ev => ev.stopPropagation());
this.dialog.addEventListener('mousedown', this.handleMouseDown.bind(this));
// 这里需要注意,为了防止鼠标移动过快出现bug,将事件代理到document身上
document.addEventListener('mousemove', this.handleMouseMove.bind(this));
document.addEventListener('mouseup', this.handleMouseUp.bind(this));
document.body.appendChild(this.dialog);
}

handleMouseDown(ev) {
this.isMoving = true;
// 鼠标按下的时候,生成一个框,给基础样式
// 位置和原来的dom重合,加定位
this.box = document.createElement('div');
this.box.style = `
width:${this.dialog.offsetWidth}px;
height:${this.dialog.offsetHeight}px;
position:absolute;
top:${this.dialog.offsetTop}px;
left:${this.dialog.offsetLeft}px;
border:1px dashed #000;
border-radius:4px;
`;
document.body.appendChild(this.box);
// 鼠标按下的时候,记录鼠标点击的位置相对于div块的坐标
this.x = ev.offsetX;
this.y = ev.offsetY;

}

handleMouseMove(ev) {
if (this.isMoving) {
// 有滚动条要把滚动条的距离算在内
let scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

// div跟着走的时候,需要把鼠标按下时的那个相对坐标算在内
this.toLeft = ev.clientX + scrollLeft - this.x;
this.toTop = ev.clientY + scrollTop - this.y;

// 设置一下拖拽边界
let maxLeft = window.innerWidth + scrollLeft - this.dialog.offsetWidth;
let maxTop = window.innerHeight + scrollTop - this.dialog.offsetHeight;
if (this.toLeft < 0) {
// 如果left小于0,则置为0,防止div从左侧拖出
this.toLeft = 0;
} else if (this.toLeft > maxLeft) {
// 如果left值大于极限,则置为极限,防止div从右侧被拖出
this.toLeft = maxLeft;
};

if (this.toTop < 0) {
this.toTop = 0;
} else if (this.toTop > maxTop) {
this.toTop = maxTop;
};

// 设置一下框的位置
this.box.style.left = this.toLeft + 'px';
this.box.style.top = this.toTop + 'px';
}
}

handleMouseUp() {
this.a += 1;
// 鼠标抬起的时候,设置一下div块的位置,跟到框所在的位置
this.dialog.style.left = this.toLeft + 'px';
this.dialog.style.top = this.toTop + 'px';
document.body.removeChild(this.box);
// 清除事件监听事件
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
this.isMoving = false;
}
};
let dialog = new Dialog('hello');
dialog.open();
这里的mousemove的监听对象修改为了document,否则如果我们拖拽速度太快会出现bug,即可能鼠标脱离了容器,然后这时就会出现问题,
只要将事件代理到了document身上就可以解决这个问题了。