拖放(三):可拖放交换的组件块

可拖放交换位置的widget组件

拖放排序可被应用的地方非常多,像标签页的排序,widget组件的互换位置,或者任意摆放的图片墙等等。

从过程上考虑,首先要能把目标元素给拖动起来(⊙_⊙),之后检测与其他元素之间的距离执行交换,最后来释放掉目标元素。

如果是使用鼠标函数拖放的话,需要将可拖动的对象设定为绝对定位,这样能更方便的操作。因此基本上只要考虑以下几方面:

  • 重绘位置
  • 距离检测
  • 序列重排
  • 拖放函数

按照上面的步骤,先来个低配版的拖放排序

位置重绘

把获取到的元素位置由相对定位转换为绝对定位,同时把当前位置的left,top值保存在position变量中,方便后面元素交换时使用。

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
var drag = (function() {
return {
data: {
wrap: '',
eles: '',
position: []
},
init: function(obj) {
var $wrap = $(obj.wrap);
var $eles = $wrap.find(obj.lists);
this.data.wrap = $wrap;
this.data.eles = $eles;
this.repaint();
},
repaint: function() {
var self = this;
var $eles = this.data.eles;
$eles.each(function(index, el) {
$(this).css({
'left': $(this).position().left,
'top': $(this).position().top
});
this.seq = $(this).index();
self.data.position.push($(this).position())
});
$eles.css('position', 'absolute');
}
}
}())
drag.init({
wrap: '#drag',
lists: '.box'
})

距离检测

距离检测的方式有很多种

一种是对鼠标坐标的监听,用当前坐标去遍历存储的元素坐标,使用left,top和元素的width, height判断鼠标是否在容器的范围内;

还有一种是把mousemove事件绑定到可交换的元素上,当满足条件时触发交换。

先说一下后一种方法,在移动过程中发现后面的元素并不能触发前面元素的事件,比较之后看的出来应该是层级的原因,因此需要将被拖放的元素z-index设置为负值。
image
image

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
···
startDrag: function() {
var self = this;
var isDraging = false,
isMoving,
box, seq, vacant;
var offset = {
x: 0,
y: 0
}
var $wrap = self.data.wrap,
$eles = self.data.eles;
$eles.on('mousedown', function(e) {
isDraging = true;
isMoving = true;
box = $(this);
vacant = box[0].seq;
offset.x = e.clientX - this.offsetLeft;
offset.y = e.clientY - this.offsetTop;
box.css({
'z-index': -1,
'opacity': 0.8
});
});
$eles.on('mousemove', function(event) {
if (isDraging && isMoving) {
isMoving = false;
$(this).animate({
'left': self.data.position[vacant].left,
'top': self.data.position[vacant].top
}, 200, function() {
isMoving = true;
});
vacant = this.seq;
this.seq = box[0].seq;
box[0].seq = vacant;
}
});
···
}

序列重排

在上面设置了一个vacant的变量,表示的是当前过程中哪一个位置是空闲的,所有的位置信息是在重绘的时候保存起来的,因此可以通过vacant的值找到对应坐标,方便设置被交换元素以及被拖动元素的坐标。

问题

当然低配版的还是会存在一些瑕疵,问题多是由于动画效果持续时产生的,一种解决方法是拷贝一份被拖放元素并设置为透明隐藏,交换时使用透明元素,过渡效果使用可见元素。