从面相过程的拖拽到面向对象的拖拽再到简易的组件拖拽

首先,是最基本的面向过程的拖拽代码

1
<div id="box"></div>
1
2
3
4
5
6
#box{
width: 100px;
height: 100px;
background-color: red;
position: absolute;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
window.onload=function(){
var oDiv=document.getElementById('box');
var disX=0;
var disY=0;
oDiv.onmousedown=function(event){
//获取事件对象
var event=event||window.event;
// disX相当于鼠标到div左侧的距离,同理disY
disX=event.clientX-oDiv.offsetLeft;
disY=event.clientY-oDiv.offsetTop;
document.onmousemove=function(event){
var event=event||window.event;
oDiv.style.left=event.clientX-disX+'px';
oDiv.style.top=event.clientY-disY+'px';
}
document.onmouseup=function () {
// 鼠标释放时事件清空
document.onmousemove=null;
document.onmouseup=null;
}
return false;
}
}

开始改写版本一

尽量不要出现函数嵌套函数

  • 可以有全局变量
  • 把onload中不是赋值的语句放在单独函数中
    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
    var oDiv=null;
    var disX=0;
    var disY=0;
    window.onload=function(){
    oDiv=document.getElementById('box');
    init()
    }
    function init() {
    oDiv.onmousedown=fnDown;
    }
    function fnDown(event){
    var event=event||window.event;
    disX=event.clientX-oDiv.offsetLeft;
    disY=event.clientY-oDiv.offsetTop;
    document.onmousemove=fnMove;
    document.onmouseup=fnUp;
    return false;
    }
    function fnMove(event){
    var event=event||window.event;
    oDiv.style.left=event.clientX-disX+'px';
    oDiv.style.top=event.clientY-disY+'px';
    }
    function fnUp() {
    document.onmousemove=null;
    document.onmouseup=null;
    }

面向对象的改写 es5

  • 全局变量就是属性
    • 函数就是方法
    • onload中创建对象
    • 改this指向问题

在ie和谷歌下,这样是可以的,但是火狐下,应为有些地方为了this指向 嵌套了一层函数,但火狐可不这样,他认为event是事件函数传递的,也就是事件后面更着的函数,这是好就需要把event当做参数传递了

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
window.onload=function(){
var d=new Drag('box');
d.init();
}
//构造函数
function Drag(id) {
this.disX=0;
this.disY=0;
this.oDiv=document.getElementById(id);
}
Drag.prototype.init=function () {
// 这里的this 指向的是Drag这个类
var _this=this;
this.oDiv.onmousedown=function () { //这里嵌套一层是为了解决若写成this.fnDown的话,下面fnDown里面的this就会变成this.oDiv,相当于this就变成div了
// 匿名函数里的this是指window,因为this指的是调用他的对象,但是匿名函数不知道是谁调用的,所以可以认为是被window调用的
_this.fnDown()
};
}
Drag.prototype.fnDown=function (event) {
var event=event||window.event;
this.disX=event.clientX-this.oDiv.offsetLeft;
this.disY=event.clientY-this.oDiv.offsetTop;
var _this=this;
document.onmousemove=function () {
_this.fnMove()
};
document.onmouseup=this.fnUp;
return false;
}
Drag.prototype.fnMove=function (event) {
var event=event||window.event;
this.oDiv.style.left=event.clientX- this.disX+'px';
this.oDiv.style.top=event.clientY- this.disY+'px';
}
Drag.prototype.fnUp=function () {
document.onmousemove=null;
document.onmouseup=null;
}

但是火狐下报错:TypeError: event is undefined

火狐的解决办法
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
window.onload = function () {
var d = new Drag('box');
d.init();
}
//构造函数
function Drag(id) {
this.disX = 0;
this.disY = 0;
this.oDiv = document.getElementById('box');
}
Drag.prototype.init = function () {
var _this = this;
this.oDiv.onmousedown = function (event) { //嵌套是为了解决this问题
var event = event || window.event;
_this.fnDown(event)
};
}
Drag.prototype.fnDown = function (event) {
this.disX = event.clientX - this.oDiv.offsetLeft;
this.disY = event.clientY - this.oDiv.offsetTop;
var _this = this;
document.onmousemove = function (event) {
_this.fnMove(event)
};
document.onmouseup = this.fnUp;
return false;
}
Drag.prototype.fnMove = function (event) {
this.oDiv.style.left = event.clientX - this.disX + 'px';
this.oDiv.style.top = event.clientY - this.disY + 'px';
}
Drag.prototype.fnUp = function () {
document.onmousemove = null;
document.onmouseup = null;
}

也可以吧init 放进构造函数里面,这样只要new 一个就可以生成拖拽了
,如下所示

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
window.onload=function(){
var d=new Drag('box');
}
//构造函数
function Drag(id) {
var _this=this;
this.disX=0;
this.disY=0;
this.oDiv=document.getElementById('box');
this.oDiv.onmousedown=function (event) { //嵌套是为了解决this问题
var event=event||window.event;
_this.fnDown(event)
};
}

Drag.prototype.fnDown=function (event) {
this.disX=event.clientX-this.oDiv.offsetLeft;
this.disY=event.clientY-this.oDiv.offsetTop;
var _this=this;
document.onmousemove=function (event) {
_this.fnMove(event)
};
document.onmouseup=this.fnUp;
return false;
}
Drag.prototype.fnMove=function (event) {
this.oDiv.style.left=event.clientX- this.disX+'px';
this.oDiv.style.top=event.clientY- this.disY+'px';
}
Drag.prototype.fnUp=function () {
document.onmousemove=null;
document.onmouseup=null;
}

es6 面向对象的改写,也可以吧init 放进构造函数里面

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
window.onload = function () {
var d = new Drag('box');
d.init();
}
// 类
class Drag {
//构造函数
constructor(id) {
this.disX = 0;
this.disY = 0;
this.oDiv = document.getElementById(id);
}
init() {
var _this = this;
this.oDiv.onmousedown = function (event) {
var event = event || window.event;
_this.fnDown(event)
};
}
fnDown(event) {
this.disX = event.clientX - this.oDiv.offsetLeft;
this.disY = event.clientY - this.oDiv.offsetTop;
var _this = this;
document.onmousemove = function (event) {
_this.fnMove(event)
};
document.onmouseup = this.fnUp;
return false;
}
fnMove(event) {
this.oDiv.style.left = event.clientX - this.disX + 'px';
this.oDiv.style.top = event.clientY - this.disY + 'px';
}
fnUp() {
document.onmousemove = null;
document.onmouseup = null;
}
}

初步总结

  • 原则
    先写出普通的写法,然后改写成面向对象的写法

    普通方法变形

    • 尽量不要出现函数嵌套函数
    • 可以有全局变量
    • 把onload中不是赋值的语句放在单独函数中

      改写面向对象

    • 全局变量就是属性
    • 函数就是方法
    • onload中创建对象
    • 改this指向问题

说了这么多,我们来封装一个拖拽组件吧

组件就该可以自自定义样式吧~~~~~,data-config写入自定义的样式,有人说你怎么怎么鸡肋,不如css里面写写快,但也是可以不写的,有默认参数,js里面已经写好了,如果data-config写了的话是可以覆盖js里面的,具体看js代码

1
2
3
4
5
6
7
<div id="box1" data-config='{"width": "100px","height": "100px","backgroundColor": "black","position": "absolute"}'></div>
<script src="./index.js"></script>
<script>
var div1 = new Drag('box1');
div1.init();
</script>

尽量让用户少写css,那你就帮他考虑周全吧

1
2
3
4
5
6
7
8
*{
padding: 0;
margin: 0;
}
div{
width: 200px;
height: 200px;
}
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
// 外层包裹防止函数被污染
(function () {
// Drag 类
class Drag {
constructor (id) {
this.disX = 0;
this.disY = 0;
this.oDiv = document.getElementById(id);
// 默认设置
this.config = {
'width': '200px',
'height': '200px',
'backgroundColor': 'red',
'position': 'absolute'
};
// 若有自定义属性,那就合并
if (this.getConfig()) {
Object.assign(this.config, this.getConfig());
}
console.log(this.config);
this.init();
}
getConfig () {
var config = this.oDiv.getAttribute('data-config');
if (config && config !== '') {
return JSON.parse(config);
} else {
return null;
}
}
init () {
var _this = this;
this.oDiv.onmousedown = function (ev) {
/* 传入_this,为了下面不在重复写 */
_this.fnDown(ev, _this);
};
// 改变设置的属性
for (const i in this.config) {
this.oDiv.style[i] = this.config[i];
}
}
/* 拖拽本体 */
fnDown (ev, _this) {
this.disX = ev.clientX - this.oDiv.offsetLeft;
this.disY = ev.clientY - this.oDiv.offsetTop;
document.onmousemove = function (ev) {
_this.fnMove(ev);
};
document.onmouseup = this.fnUp;
/* 阻止默认事件 */
return false;
}
fnMove (ev) {
this.oDiv.style.left = ev.clientX - this.disX + 'px';
this.oDiv.style.top = ev.clientY - this.disY + 'px';
};
fnUp () {
document.onmousemove = null;
document.onmouseup = null;
}
}
window.Drag = Drag;
})();

你说啥??不支持手机端??那就来支持一下吧

支持的不够怎么完美,见谅。。

1
2
3
4
5
6
7
8
9
10
// 在fnDown里面先判断一下
// 判断是否为手机端
var touch;
if (ev.touches) {
touch = ev.touches[0];
} else {
touch = ev;
}
this.disX = touch.clientX - this.oDiv.offsetLeft;
this.disY = touch.clientY - this.oDiv.offsetTop;

pc上的web页面鼠 标会产生onmousedown、onmouseup、onmouseout、onmouseover、onmousemove的事件,但是在移动终端如 iphone、Touch、ipad,android上的web页面触屏时会产生ontouchstart、ontouchmove、ontouchend、ontouchcancel 事件,分别对应了触屏开始、拖拽及完成触屏事件和取消。
当按下手指时,触发ontouchstart;
当移动手指时,触发ontouchmove;
当移走手指时,触发ontouchend。

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
// 原理还是一样的
// js代码如下
(function () {
// Drag 类
class Drag {
constructor(id) {
this.disX = 0;
this.disY = 0;
this.oDiv = document.getElementById(id);
// 默认设置
this.config = {
'width': '200px',
'height': '200px',
'backgroundColor': 'red',
'position': 'absolute'
};
// 若有自定义属性,那就合并
if (this.getConfig()) {
Object.assign(this.config, this.getConfig());
}
console.log(this.config);
this.init();
}
getConfig() {
var config = this.oDiv.getAttribute('data-config');
if (config && config !== '') {
return JSON.parse(config);
} else {
return null;
}
}
init() {
var _this = this;
// pc端
this.oDiv.onmousedown = function (ev) {
/* 传入_this,为了下面不在重复写 */
_this.fnDown(ev, _this);
};
// 移动端
this.oDiv.ontouchstart=function(ev){
_this.fnDown(ev, _this);
}
// 改变设置的属性
for (const i in this.config) {
this.oDiv.style[i] = this.config[i];
}
}
/* 拖拽本体 */
fnDown(ev, _this) {
// 判断是否为手机端
var touch;
if (ev.touches) {
touch = ev.touches[0];
} else {
touch = ev;
}
this.disX = touch.clientX - this.oDiv.offsetLeft;
this.disY = touch.clientY - this.oDiv.offsetTop;
// pc
document.onmousemove = function (ev) {
_this.fnMove(ev);
};
// 移动端
document.ontouchmove = function (ev) {
_this.fnMove(ev);
};
document.onmouseup = this.fnUp;
document.ontouchend = this.fnUp;
/* 阻止默认事件 */
return false;
}
fnMove(ev) {
var touch;
if (ev.touches) {
touch = ev.touches[0];
} else {
touch = ev;
}
this.oDiv.style.left = touch.clientX - this.disX + 'px';
this.oDiv.style.top = touch.clientY - this.disY + 'px';
};
fnUp() {
document.onmousemove = null;
document.ontouchmove = null;
document.onmouseup = null;
document.ontouchend = null;
}
}
window.Drag = Drag;
})();

传送门:点击查看演示
点击查看源码


从面相过程的拖拽到面向对象的拖拽再到简易的组件拖拽
http://example.com/2018/02/21/从面相过程的拖拽到面向对象的拖拽再到简易的组件拖拽/
作者
shoukailiang
发布于
2018年2月21日
许可协议