vue实现拖拽排序
在业务中列表拖拽排序是比较常见的需求,常见的JS拖拽库有Sortable.js,Vue.Draggable等,大多数同学遇到这种需求也是更多的求助于这些JS库,其实,使用HTML原生的拖放事件来实现拖拽排序并不复杂,结合Vue的transition-group,还能快速的给排序添加过渡动画。
HTML5拖放api
1. 设置元素为可拖放
为了使元素可拖动,把 draggable 属性设置为 true :
<div draggable="true">能被拖放的元素</div>
2. 拖放事件
拖放涉及到两种元素,一种是被拖拽元素(源对象),一种是放置区元素(目标对象)。如下图所示,按住A元素往B元素拖拽,A元素即为源对象,B元素即为目标对象。
需要注意的是:dragenter和dragover事件的默认行为是拒绝接受任何被拖放的元素。因此,我们要在这两个拖放事件中使用preventDefault来阻止浏览器的默认行为;而且目标对象想要变成可释放区域,必须设置dragover 和 drop 事件处理程序属性。
基于vue的拖拽排序
先不考虑排序动画,解释一下实现思路:
1 由于拖动是实时的,所以没有使用drop而是使用了dragenter触发排序。 2 在源对象开始被拖拽时记录其索引dragIndex,当它进入目标对象时(对应dragenter事件),将其插入到目标对象的位置。 3 其中dragenter方法中有一个判断this.dragIndex !== index(index为当前目标对象的索引),这是因为源对象同时也是目标对象,当没有这个判断时,源对象开始被拖拽时就会立刻触发自身的dragenter事件,这是不合理的。
有动画的拖拽排序
把HTML的ul元素改为transition-group,在CSS中新增一个过渡transition: transform .3s;,就可以实现有动画的拖拽排序
改造成可复用的组件
<template>
<transition-group name="drag" class="list" tag="ul">
<li
@dragstart="dragstart(index)"
@dragenter="dragenter($event, index)"
@dragend="dragend"
@dragover.prevent
:draggable="draggable"
v-for="(item, index) in list"
:key="item.id"
class="list-item"
>
<slot :scope="item" :index="index"></slot>
</li>
</transition-group>
</template>
<script>
export default {
name: "DragList",
model: {
prop: "data",
event: "change",
},
props: {
// 唯一的key值是id
data: {
type: Array,
default: () => [],
},
draggable: {
type: Boolean,
default: false,
},
},
data() {
return {
dragIndex: "",
};
},
computed: {
list() {
return [...this.data];
},
},
methods: {
// 拖拽元素(源对象)
dragstart(index) {
if (!this.draggable) return;
this.dragIndex = index;
},
// 目标元素
dragenter(e, index) {
e.preventDefault();
if (!this.draggable) return;
// 避免源对象触发自身的dragenter事件
if (this.dragIndex !== index) {
const moving = this.list[this.dragIndex]; // 拖拽元素
this.list.splice(this.dragIndex, 1); // 删除拖拽元素
this.list.splice(index, 0, moving); // 在目标元素中追加拖拽元素
// 排序变化后目标对象的索引变成源对象的索引
this.dragIndex = index;
this.$emit("change", this.list);
}
},
dragover(e) {
e.preventDefault();
},
dragend() {
this.$emit("dragend");
},
},
};
</script>
<style lang="less" scoped>
.list {
list-style: none;
.drag-move {
transition: transform 0.3s;
}
.list-item {
// cursor: move;
// width: 300px;
// background: #ea6e59;
// border-radius: 4px;
// color: #fff;
// margin-bottom: 6px;
// height: 50px;
// line-height: 50px;
// text-align: center;
}
}
</style>
文档信息
版权声明:可自由转载(请注明转载出处)-非商用-非衍生
发表时间:2023年10月25日 08:56