dragToSort记一次排序组件开发

前言

有这么一种人,很多地方都会需要他,有这么一种人他一辈子只做一件事,并且做的足够好,在任何地方都能落地生根,服务大众。比如说保洁阿姨,也就是说这里被需求的它是大众化的,可被抽象化的,如果我们用程序中的专有名词来形容可以为组件,组件的存在就是干一件事,有自己存在的目标( 意义 ),有自己的行为,自己的动作,甚至这里的组件是可以被调用者配置的,就好像保洁阿姨是可以被公司分配到不同的工作地点,薪资也是可配置的。这篇文章就是讲如何将一个组件从0到1的过程( 注意暂时这个组件不会涉及到配置部分 )

需求

源码地址

提前感受1
提前感受2

你可以点击上面的两个链接提前感受一下这个插件可能要完成的功能,以及点击源码地址查看源代码。其实非常的平常,他要做的事就是允许用户通过鼠标拖拽来改变页面上某些元素的位置,从而完成类似排序的功能

使用方式

先引入dragToSort.js文件

  1. 每个组件都有自己的html结构,所以需要遵循以下的html结构
1
2
3
4
<div id="dragWrap" class="clear">
<!--将要拖拽的元素放到#dragWrap中 作为他的子元素-->
...
</div>
  1. 调用 以下两句代码既可以完成以上所诉需求
1
2
var dragTS = new DragToSort();
dragTS.init();

源代码分析

插件以面向对象的形式完成,构造函数内部有一些原型上需要用到的一些数据,挂在到原型上面的方法完成插件的主要功能。

构造函数

构造函数 主要存储插件方法中需要用到的数据

1
2
3
4
5
6
function DragToSort() {
var parent = document.getElementById('dragWrap');
this.eles = [].slice.call(parent.children);
this.elesPos = this.getPos();
this.iZIndex = 2;
};

原型
>

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
DragToSort.prototype = {
constructor: DragToSort, // 重新指向DragToSort
init: function () {
//初始化
},
getPos: function () {
// 得到需要拖拉排序的位置信息
},
css: function () {
// 简易的样式操作函数
},
getStyle: function () {
// 获取元素的行间样式 封装
},
startMove: function (){
// 动画函数封装 用来给拖拽添加动画效果
},
setPos: function () {
// 定位转换函数 因初始情况下元素是浮动布局,但是这里的插件要完成的功能需要元素定位( 绝对定位 )
},
crash: function () {
// 检查两个物体是否发生碰撞
},
distance : function () {
// 计算两个物体之间的距离
},
nearObj : function () {
// 寻找与当前拖拽元素相碰正的元素中最近的元素
},
drag : function () {
// 拖拽函数封装
}

接下来开始详细解释各段代码

getPos

通过对需要拖拽的元素,进行遍历,用数组将其位置信息存储起来,并给每个元素设置一个初始的索引值(为什么要用到索引值,后面会说到)

1
2
3
4
5
6
7
8
9
10
11
function (){
var pos = [];
this.eles.forEach( function (item ,index) {
pos.push({
left : item.offsetLeft,
top : item.offsetTop
})
item.index = index;
})
return pos;
}

css

设置元素样式的的简易函数封装,接受一个元素对象,和设置css样式的对象

1
2
3
4
5
function (objEle, obj) {
for(var attr in obj){
objEle['style'][attr] = obj[attr];
}
}

getStyle

获取元素样式兼容函数封装

1
2
3
4
5
6
7
function (obj, attr) {
if (obj.currentStyle) {
return obj.currentStyle[attr]; // chrome ie ...
}else{
return getComputedStyle(obj, false)[attr]; // firefox ...
}
}

startMove

动画函数

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
function (obj, json, fnEnd){
var _this = this;
clearInterval(obj.timer);
obj.timer=setInterval(function (){
var bStop=true;
for(var attr in json){
var cur=0;
if(attr=='opacity'){
cur=Math.round(parseFloat(_this.getStyle(obj, attr))*100);
}else{
cur=parseInt(_this.getStyle(obj, attr));
}
var speed=(json[attr]-cur)/6;
speed=speed>0?Math.ceil(speed):Math.floor(speed);
if(cur!=json[attr]){
bStop=false;
}
if(attr=='opacity'){
obj.style.filter='alpha(opacity:'+(cur+speed)+')';
obj.style.opacity=(cur+speed)/100;
}else{
obj.style[attr]=cur+speed+'px';
}
}
if(bStop){
clearInterval(obj.timer);
if(fnEnd)fnEnd();
}
}, 30);
}

setPos

将浮动布局转化为定位布局

1
2
3
4
5
6
7
8
9
10
11
12
function () {
//定位转换 absolute
var _this = this;
_this.eles.forEach( function (item,index) {
_this.css(item,{
position : 'absolute',
left : _this.elesPos[index]['left'] + 'px',
top : _this.elesPos[index]['top'] + 'px',
margin : 0
});
})
}

crash

检查两个物体是否发生碰撞,要检查两个物体是否发生碰撞,值需要检查它们的位置关系就可以,也就是两物体没有重叠区域。如图,另一个物体处于红色圆圈位置便不会发生碰撞,反之

困

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function (obj, obj2) {
//检查两个物体是否发生碰撞
// obj1
var t1 = obj.offsetTop;
var l1 = obj.offsetLeft;
var b1 = obj.offsetHeight + t1;
var r1 = obj.offsetWidth + l1;
// obj2
var t2 = obj2.offsetTop;
var l2 = obj2.offsetLeft;
var b2 = obj2.offsetHeight + t2;
var r2 = obj2.offsetWidth + l2;
//检查
if( r1<l2 || r2<l1 || b1<t2 || b2<t1 ){
return false;//未碰撞
}else{
return true;
}
}