鲁迅说过:“若是你的技术很差,那么你的姿式要多。”html
公司的项目最近给侧边栏加了拖动排序的效果,相似于下面这种效果(鲁迅:不是个人下面),是下面的图?:html5
说来惭愧,上图不是我写的,否则我还能吹一波,没有看代码,但闭着眼就能猜到用的是html5的drag系列的api,相关的API能够看MDN上的介绍,我把文档拷一下作个介绍吧==git
drag
: 当元素或者选择的文本被拖动时触发drag
事件 (每几百毫秒)。dragstart
: 当用户开始拖动一个元素或者一个选择文本的时候dragstart
事件就会触发。通常在这个动做里面处理被拖动元素的样式。dragend
: 拖动操做结束时触发,即松开鼠标操做。dragenter
: a拖到b中的时候触发。dragover
: 拖动过程不断触发。dragexit
: 元素不是被拖动目标时。dragleave
: a拖到b,不释放鼠标,再拖到c,离开b的时候触发。drop
: 当一个元素或是选中的文字被拖拽释放到一个有效的释放目标位置时上面的大部分都是对样式之类的处理,官方也给了例子,排除掉drag
和dragover
这两个触发大户,大概的顺序是这样的:github
首先,我确定是实现了效果的,以下图所示的:chrome
粗略介绍下流程吧,首先须要肯定的是应该是什么效果,打个比方,我把a拖到了b上,b能很明显的感受到,“啊,有什么奇怪的东西进入个人体内了”。可是b并不知道a是想在前面仍是想在后面...这时候你就发现问题了吧,对!提问:“a和b到底谁在上面谁在下面?”因此咱们刚开始要约定一个规范,把上面的a拖向下面的b,那么就把a排在b的下面,反之,把下面的a拖向上面的b,那么就把a放在b的上面~首先我须要这四个对象:api
let dragObj, enterObj, dragIndex, enterIndex; // dragObj 被拖动的对象a,整个拖动过程它是不会变的 // enterObj 最终进入的对象b,在drop里能够获取到 // dragIndex a在列表中的下标 // enterIndex b在列表的下标
经过比较dragIndex
和enterIndex
的大小来决定a和b的关系,剩下的就是对dom进行操做了:数组
if (dragIndex < enterIndex) { dragObj.remove(); enterObj.after(dragObj); } else if (dragIndex > enterIndex) { dragObj.remove(); enterObj.before(dragObj); }
直接看看效果吧,也能够作个比较:dom
预览效果
使用到的api:onmousedown
, onmousemove
, onmouseup
,其实这些你们多多少少应该会用过,但我仍是在这里粗略介绍下:动画
onmousedown
: 在当前元素上点击鼠标按键时会触发mousedown
事件,有人问了,那这个和click
有什么区别呢,区别是 click = mousedown + mouseup,而后是,click鼠标左键触发,mousedown鼠标点击即会触发。onmousemove
: 当用户在当前元素上移动鼠标时会触发mousemove事件,就是“拖动元素。onmouseup
: 当用户在当前元素上放开鼠标某个按键时会触发mouseup事件。整个实现过程也比上面的那一种要复杂的多,不一样与drag
,drag
本身能感知元素从a移动到了b中或者从b中离开,而鼠标事件不行。
这样一个列表:
假设我点了“小强”,“小强”被我选中,我怎么去实现拖动的效果呢?元素自己是没有拖动这个说法的,我能作到的就是动态的改变它的位置,那就是在onmousemove
的时候动态的去改变了,改变元素位置且不影响到其余元素排布的,那就是absolute
了。先实现拖动的效果,大概就是这样:
//在onmousedown的时候记下必要的信息,将小强的position改为absolute const startY = event.clientY; //在onmousemove记录下鼠标在y轴上移动的距离,横行的话就是x const currentTop = parseInt(startTop) + (moveY - startY);
这样基本的拖动问题是解决了,元素最起码是能够拖动起来了。这里须要注意的是,只有“小强”的位置是absolute
,这时候“小明”就会被小强遮住,因此咱们这里须要一个元素“占位”:
<div class="item hold"><div>
各位乘客系好安全带,我要开车了...
理想的效果应该是这样的,“小强”向下拖动,距离“小明”底部小于一半时,“小明”就应该给“小强”让路,“小明”要知道小强何时进来了,而后hold
元素向下移动一格,说明小强“此时”已经在“小明”下面了:
currentTop > itemHeight + itemHeight / 2;
记录高度实在是不必,我只须要记录“小强” 当前 被拖动到“第几位”就能够了:
currentIndex = Math.ceil((currentTop - itemHeight / 2) / itemHeight);
只要拿到onmousedown
时的“小强”的“index”,与当前的比较,只要二者不相同,就须要移动hold
的位置,hold
元素的位置受当前index的影响:
if (previousIndex !== currentIndex) { //... }
此处省略的代码都是dom的操做,不作太多说明。此时咱们并无对拖动的元素作限制,元素可以被拖到盒子的外面,这样确定是不能够的,作一个限制:
if (currentIndex < 0) { currentIndex = 0; } else if (currentIndex > listLength - 1) { currentIndex = listLength - 1; }
这样不会限制的用户拖动的操做,咱们只须要控制住index就行了,这样处理也方便的多。
在用户onmouseup
的时候须要对监听器进行销毁:
document.onmousemove = null; document.onmouseup = null;
相似于$$
和querySelectorAll
获取到的"数组"并非“数组”,它有数组的部分特性,经过下图咱们能够知道它是一个NodeList
,并非Array
,它是有forEach
方法的,可是它并无map
、find
等一系列方法,使用的时候还需注意。
细节仍是看源码吧 我在刚开始写的时候碰到不少坑,dom操做的时候更是,尤为在上下来回拖动的时候,具体的再也不多讲,由于实在有点晚了。对不起,我今晚玩游戏玩到十一点半,我有罪==,洗个澡啥的,弄的很晚。
享受coding,热爱生活,拜~