我写的微信小程序“通知中心”有一个打卡通知的功能,里面的项目支持纵向拖拽排序,是使用 wxs 实现的。具体来说,是在列表每一个项目的 touchstart
、touchmove
、touchend
事件中绑定来自 wxs 的事件处理函数,在事件函数中依据滑动的位置实时改变列表项目中的 css 属性(transform
、transition
),从而使之产生被拖动且有动画的效果。把事件函数写在 wxs 这种做法确实流畅,但 wxs 写起来有点麻烦,我也很少写,不利于我后续维护。
最近看到被称为光神的博主写的《手写一个性能较好的拖拽排序》,启发到我了,启发点主要是拖动时不实时修改列表中的项目,而是修改它的替身。具体点说,把被拖动项目复制一个替身出来,把替身设为 position: fixed
,拖动时,列表中的项目都不动,只改变替身的位置,当替身到达某个临界点需要列表中的项目挪开时,才改变列表中项目的位置,无论是位置的改变还是改变时的动画,都通过 css 实现。
博主的例子同时支持纵向、水平两个方向的拖拽,且支持动画:
我看了博主的文章后改写了通知中心的拖拽排序功能,为了便于后续维护,没有增加动画效果,拖动过程中也不会实时调整项目位置,当松手时,再对位置进行替换:
光神的对写一个性能较好的拖拽排序的流程总结:
pointerdown 的时候 clone 一个新元素,pointermove 的时候改变它的 translate3d 来实现拖拽,pointerup 的时候删掉它。
通过 getBoundingClientRect 取出每个元素的位置,pointermove 的时候判断指针在哪个元素,然后通过 translate3d 移动前后位置之间的元素,还要设置 transition 的过渡效果。
最后 pointerup 的时候通过 insertBefore 完成元素移动,也就是排序的效果。
自始至终,我们只改变了一次 dom 顺序,拖拽过程中只是设置了 translate 位置,这种实现方式性能会比较好。