likes
comments
collection
share

穿梭的拖拽排序,轻松拿捏

作者站长头像
站长
· 阅读数 37

前言

穿梭的拖拽排序,轻松拿捏

功能实现思路

穿梭的列表排序实现思路也是如此,因为不管你几个列表,你还是1对1的交换,所以还是记录2个索引即可,不同的是你要知道是哪一个列表的哪个索引,而不是简单的一个列表中的哪个索引

如何判断是哪一个列表的哪一个索引呢?

有很多思路,比如在不同的列表元素上加上不同的group,然后在对应的事件中根据group区分,这里我说下我的思路

尝试猜测,统一处理

尝试猜测的意思:我们在交换的过程中会拿到2个event,分别是鼠标按下的时的event鼠标移动时的event,我们可以根据event获取到元素id,在根据元素id2个列表中都获取一下,哪个列表能获取到数据就知道当前元素所在的列表了,伪代码如下

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 函数执行

pauseresume函数是内部统一包装好的,感兴趣的可以去看源码,最终效果如下

这里省略了鼠标按下时添加高亮效果鼠标抬起时取消高亮效果的代码,为什么省略,因为比较简单(主要因为懒🧐)

结语

如有错误之处,请指正,谢谢大家~

转载自:https://juejin.cn/post/7269063053877395512
评论
请登录