Vue3 custom drag and drop sorting components, with transition effects

 Vue3 custom drag and drop sorting component

Note: This component can only be used for sorting, and there is no cross-element drag and drop, but it may continue to be improved later

1. Define components

<template>
	<TransitionGroup tag="div" name="fade" class="container">
		<div
			v-for="(item, index) in thisList"
			:key="item"
			class="item"
			draggable="true"
			@dragstart="dragstart(index)"
			@dragenter="drag(index)"
			@dragend="dragend">
			<slot :item="item"></slot>
		</div>
	</TransitionGroup>
</template>
<script setup lang="ts">
import { ref } from "vue";
interface props {
	list: any[];
	sj?: string;
	bezier?: string;
}
const prop = withDefaults(defineProps<props>(), {
	// 过渡时间;为0s,就没有过渡效果
	sj: "0.5s",
	// 贝塞尔曲线
	bezier: "ease",
});
let thisList = ref(prop.list);

// 哪条数据 要调换元素的下标
let startIndex: number;
// 哪条数据 调换到目标的下标
let endIndex: number | undefined;
//获取点击的下标
const dragstart = (index: number) => {
	startIndex = index;
};
// 开始调换
const drag = (index: number) => {
	// 解决反复拖拽出现的问题,
	if (endIndex !== undefined && endIndex !== startIndex) {
		startIndex = endIndex;
	}
	// 目标的位置
	endIndex = index;
	// 是否相隔1
	let apartadvOne = Math.abs(startIndex - endIndex) === 1;

	if (startIndex === endIndex) {
		// 被拖拽的目标 和 置放的位置 相等
		return;
	} else if (!apartadvOne && startIndex !== endIndex) {
		// 相隔超过1 相隔过大(解决快速拖拽)
		apartadvOversize(startIndex, endIndex);
		return;
	}
	// 被拖拽的目标 和 置放的位置 不相等
	if (startIndex !== endIndex) {
		thisList.value.splice(
			endIndex,
			1,
			...thisList.value.splice(startIndex, 1, thisList.value[endIndex])
		);
		// 新的一轮改变,
		// 上面调换后,要调换元素的下标 就是 这次的endIndex
		startIndex = endIndex;
	}
};
// 结束调换
const dragend = () => {
	endIndex = undefined;
};

const apartadvOversize = (startIndex: number, endIndex: number) => {
	let end: number;
	//   向上拖还是向下拖             向上             向下
	end = startIndex > endIndex ? startIndex - 1 : startIndex + 1;
	// 调换
	thisList.value.splice(end, 1, ...thisList.value.splice(startIndex, 1, thisList.value[end]));
	// 相隔1 ,不递归,退出
	if (Math.abs(startIndex - endIndex) === 1) {
		startIndex = endIndex;
		return;
	}
	// 向上拖还是向下拖  继续递归
	if (startIndex > endIndex) {
		apartadvOversize(startIndex - 1, endIndex);
	} else {
		apartadvOversize(startIndex + 1, endIndex);
	}
};
</script>

<style>
.container {
	width: 100%;
	height: 100%;
	position: relative;
	padding: 0;
	display: flex;
	flex-direction: column;
}

.item {
	cursor: move;
	user-select: none;
}

/* 1. 声明过渡效果 */
.fade-move,
.fade-enter-active,
.fade-leave-active {
	transition: all v-bind("sj") v-bind("bezier");
}

/* 2. 声明进入和离开的状态 */
.fade-enter-from,
.fade-leave-to {
	opacity: 0;
}

/* 3. 确保离开的项目被移除出了布局流
      以便正确地计算移动时的动画效果。 */
.fade-leave-active {
	position: absolute;
}
</style>

2. Use components

If it is not helpful to use the height in the slot, you can try using line-height

<template>
	<DragSort :list="tableData" #default="{ item }">
		<div id="div1">{
   
   { item }}</div>
	</DragSort>
</template>

<script lang="ts" setup>
import DragSort from "./components/dragSort.vue";
import { ref } from "vue";
//数据
let tableData = ref<any[]>([]);
for (let i = 0; i < 10; i++) {
	tableData.value.push({
		id: i,
		name: "Tom",
		date: `2023-${Math.ceil(Math.random() * 12 + 1)}-${Math.ceil(Math.random() * 28 + 1)}`,
		tag: "Home",
	});
}
</script>
<style>
#div1 {
	width: 500px;
	height: 30px;
	padding: 2px;
	border: 1px solid #aaaaaa;
}

</style>

3. Rendering

20230217_100735

Guess you like

Origin blog.csdn.net/weixin_59916662/article/details/129078803