穿梭的拖拽排序,轻松拿捏
前言
功能实现思路
穿梭的列表排序实现思路也是如此,因为不管你几个列表,你还是1对1
的交换,所以还是记录2个索引即可,不同的是你要知道是哪一个列表的哪个索引
,而不是简单的一个列表中的哪个索引
如何判断是哪一个列表的哪一个索引呢?
有很多思路,比如在不同的列表元素上加上不同的group
,然后在对应的事件中根据group
区分,这里我说下我的思路
尝试猜测,统一处理
尝试猜测的意思:我们在交换的过程中会拿到2个event
,分别是鼠标按下的时的event
和鼠标移动时的event
,我们可以根据event
获取到元素id
,在根据元素id
在2个列表中都获取一下
,哪个列表能获取到数据就知道当前元素所在的列表了,伪代码如下
function getIndexAndListByEvent(event: EnhancedMouseEvent) {
const element = event.target!
const id = element.getAttribute('id') as string
const list1Getter = unref(id2IndexByList1Getter) // 列表1的id和索引map表
const list2Getter = unref(id2IndexByList2Getter) // 列表2的id和索引map表
if (list1Getter.has(id)) {
return {
index: list1Getter.get(id)!,
list: unref(list1Ref),
}
}
if (list2Getter.has(id)) {
return {
index: list2Getter.get(id)!,
list: unref(list2Ref),
}
}
}
统一处理的意思:我们已经知道了索引和列表数据
,所以我们可以这样写交换方法
function swap(startEvent: EnhancedMouseEvent, moveEvent: EnhancedMouseEvent) {
const startIndexAndList = getIndexAndListByEvent(startEvent)
const targetIndexAndList = getIndexAndListByEvent(moveEvent)
if (!startIndexAndList || !targetIndexAndList) return
const { index: startIndex, list: list1 } = startIndexAndList
const { index: targetIndex, list: list2 } = targetIndexAndList
if (list1 === list2 && startIndex === targetIndex) return
// swap
const temp = list1[startIndex]
list1[startIndex] = list2[targetIndex]
list2[targetIndex] = temp
}
这样实现的思路优缺点我说明一下
优点:不需要判断2个索引
是不是在一个列表,然后单独的处理
缺点:需要id
唯一
功能实现
布局代码
在实现之前,我们可以把布局先写出来,布局代码如下
<script setup lang="tsx">
import type { EnhancedMouseEvent } from '@drag-drop/core'
import { useDragDrop } from '@drag-drop/core'
import { sortPlugin } from '@drag-drop/plugin-sort'
import { useEventListener } from '@drag-drop/shared'
import { computed, ref, unref } from 'vue'
interface Item {
id: string
name: string
color: string
}
const baseBackgroundColor = 'skyblue'
const swapBackgroundColor = 'pink'
const list1Ref = ref<Item[]>([
{ id: '1', name: '1', color: baseBackgroundColor },
{ id: '2', name: '2', color: baseBackgroundColor },
{ id: '3', name: '3', color: baseBackgroundColor },
{ id: '4', name: '4', color: baseBackgroundColor },
{ id: '5', name: '5', color: baseBackgroundColor },
{ id: '6', name: '6', color: baseBackgroundColor },
{ id: '7', name: '7', color: baseBackgroundColor },
])
const list2Ref = ref<Item[]>([
{ id: '8', name: '8', color: baseBackgroundColor },
{ id: '9', name: '9', color: baseBackgroundColor },
{ id: '10', name: '10', color: baseBackgroundColor },
{ id: '11', name: '11', color: baseBackgroundColor },
{ id: '12', name: '12', color: baseBackgroundColor },
{ id: '13', name: '13', color: baseBackgroundColor },
{ id: '14', name: '14', color: baseBackgroundColor },
])
const id2IndexByList1Getter = computed(() => {
const list = unref(list1Ref)
return list.reduce((p, c, i) => {
p.set(c.id, i)
return p
}, new Map<string, number>())
})
const id2IndexByList2Getter = computed(() => {
const list = unref(list2Ref)
return list.reduce((p, c, i) => {
p.set(c.id, i)
return p
}, new Map<string, number>())
})
</script>
<template>
<TransitionGroup tag="div" class="container" name="list">
<div v-for="item in list1Ref" :id="item.id" :key="item.id" class="item" :style="{ background: item.color }">
{{ item.name }}
</div>
</TransitionGroup>
<TransitionGroup tag="div" class="container" name="list">
<div v-for="item in list2Ref" :id="item.id" :key="item.id" class="item" :style="{ background: item.color }">
{{ item.name }}
</div>
</TransitionGroup>
</template>
<style scoped>
.container{
display:inline-flex;
flex-direction:column;
gap:10px;
margin:100px 0 0 30px;
padding:20px;
border:1px solid #ccc;
}
.item{
display:flex;
align-items:center;
justify-content:center;
width:200px;
height:40px;
user-select:none;
border-radius:5px;
}
</style>
以上代码运行后效果如下
引入 sort 插件
<script setup lang="tsx">
import type { EnhancedMouseEvent } from '@drag-drop/core'
import { useDragDrop } from '@drag-drop/core'
import { sortPlugin } from '@drag-drop/plugin-sort'
...
function canDraggable(event: EnhancedMouseEvent) {
return !!event.target?.classList.contains('item')
}
const context = useDragDrop({
canDraggable,
})
context.use(sortPlugin({
swap, // 这里需要用户去实现
}))
</script>
说明一下
实现 swap 函数
我们在上面的 功能实现思路
这块已经说明了应该如何编写,这里我将它引入过来,代码如下
<script setup lang="tsx">
import type { EnhancedMouseEvent } from '@drag-drop/core'
import { useDragDrop } from '@drag-drop/core'
import { sortPlugin } from '@drag-drop/plugin-sort'
import { useEventListener } from '@drag-drop/shared'
import { computed, ref, unref } from 'vue'
function canDraggable(event: EnhancedMouseEvent) {
return !!event.target?.classList.contains('item')
}
const context = useDragDrop({
canDraggable,
})
context.use(sortPlugin({
swap,
}))
function getIndexAndListByEvent(event: EnhancedMouseEvent) {
const element = event.target!
const id = element.getAttribute('id') as string
const list1Getter = unref(id2IndexByList1Getter)
const list2Getter = unref(id2IndexByList2Getter)
if (list1Getter.has(id)) {
return {
index: list1Getter.get(id)!,
list: unref(list1Ref),
}
}
if (list2Getter.has(id)) {
return {
index: list2Getter.get(id)!,
list: unref(list2Ref),
}
}
}
function swap(startEvent: EnhancedMouseEvent, moveEvent: EnhancedMouseEvent) {
const startIndexAndList = getIndexAndListByEvent(startEvent)
const targetIndexAndList = getIndexAndListByEvent(moveEvent)
if (!startIndexAndList || !targetIndexAndList) return
const { index: startIndex, list: list1 } = startIndexAndList
const { index: targetIndex, list: list2 } = targetIndexAndList
if (list1 === list2 && startIndex === targetIndex) return
// swap
const temp = list1[startIndex]
list1[startIndex] = list2[targetIndex]
list2[targetIndex] = temp
}
</script>
上述代码实现后页面可以实现穿梭交换了,但是并没有动画效果,我们只需要在style
中加一行代码即可
<template>
<TransitionGroup ... name="list">
...
</TransitionGroup>
<TransitionGroup ... name="list">
...
</TransitionGroup>
</template>
<style scoped>
.list-move{
transition: transform 0.1s linear;
}
</style>
const context = useDragDrop({
canDraggable,
})
const { pause, resume } = context.use(sortPlugin({
swap,
}))
function swap(startEvent: EnhancedMouseEvent, moveEvent: EnhancedMouseEvent) {
...
// swap
const temp = list1[startIndex]
list1[startIndex] = list2[targetIndex]
list2[targetIndex] = temp
pause() // 暂停 swap 函数执行
}
useEventListener('transitionend', resume) // 动画结束后恢复 swap 函数执行
pause
和resume
函数是内部统一包装好的,感兴趣的可以去看源码,最终效果如下
这里省略了鼠标按下时添加高亮效果
及鼠标抬起时取消高亮效果
的代码,为什么省略,因为比较简单(主要因为懒🧐)
结语
如有错误之处,请指正,谢谢大家~
转载自:https://juejin.cn/post/7269063053877395512