vue拖拽删除实现

拖拽删除

实现效果

在这里插入图片描述

背景

自营上传图片,但是需要排序和删除功能,所以用到了h5的拖拽
在这里插入图片描述

源元素: 即被拖拽的元素。

目标元素: 即合法的可释放元素。

每个事件的事件主体都是两者之一。

拖拽事件

在这里插入图片描述

触发顺序及次数

被拖拽元素,事件触发顺序是 dragstart->drag->dragend;对于目标元素,事件触发的顺序是 dragenter->dragover->drop/dropleave。

其中drag和dragover会分别在源元素和目标元素反复触发。整个流程一定是dragstart第一个触发,dragend最后一个触发。

这里还有一个注意的点,如果某个元素同时设置了dragover和drop的监听,那么必须阻止dragover的默认行为,否则drop将不会被触发。

  • 站在源元素和目标元素的角度来看,就是dragstart 可以获取到当前的信息,源dragenter可以获取到目标元素,dragend,此时拖拽结束,可以做碰撞检测的逻辑

移动位置

api使用

这里我利用 dragstart 记录源数据,和索引。利用dragenter记录目标元素的数据,和索引,等拖拽结束,利用dragend做位置移动逻辑。

排序逻辑 (核心逻辑)

  • 利用splice, 先删除源数据,然后在目标元素之后,添加新元素。实现拖拽排序

比如将a 和b交换位置

  1. 获取a和b的索引
  2. 先将a删除
  3. 在b之后,再添加a
let oldData = 'a'; // 拖动那个元素
let newData = 'b'; // 要移动到那
let arr = ['d', 'a', 'c', 'b', 'e'];
let indexA = arr.indexOf(oldData); // 0
let indexB = arr.indexOf(newData); // 1
arr.splice(indexA, 1);
arr.splice(indexB, 0, oldData);
console.log('arr', arr); //[ 'd', 'c', 'b', 'a', 'e' ]

代码逻辑

<template>
	<div class="mg-top-wrap">
			<!-- @dragover="dragover($event)" -->
		<div
			class="drag-sort-box"
		>
			<div
				class="drag-sort-item"
				v-for="(item, index) in images"
				:key="item"
				:draggable="true"
				@dragstart="dragstart(item, index)"
				@dragenter="dragenter(item, $event)"
				@dragend="dragend(item, $event)"
				@dragover="dragover($event)"
				@mouseover="mouseover(index)"
				@mouseout="mouseout()"
			>
				<p
					:class="index === indexDel ? 'moxsind' : ''"
					@click="delPicHandler()"
				></p>

				<img :src="item.image_url" />
			</div>
		</div>
		<wd-button class="" @click="chooseImage()" :loading="loading">
			上传图片
			<input
				@change="uploadImg($event)"
				type="file"
				style="display: none"
				id="upload"
			/>
		</wd-button>
		<div>记得去图文详情保存哦~</div>
	</div>
</template>

<script setup>
import { onMounted, reactive, ref, watch } from 'vue';
import axios from '../../../utils/ajax';
const props = defineProps({
	shopImage: Array, // 图片列表
});
const images = ref([]);

const emit = defineEmits(['change']);

watch(
	() => props.shopImage,
	(val) => {
		console.log('数据是否发生变化', val);
		if (val.length) {
			images.value = props.shopImage;
		}
	}
);

const itemClass = ref('');
// images.value = images.value.map((v, i) => (v = v + '?index=' + i)); //不重复key
console.log('sssss', images.value);
let oldData = null;
let newData = null;

const dragstart = (value, index) => {
	oldData = value;
	console.log('开始的值', images.value);
};
const dragenter = (value, e) => {
	newData = value;
	e.preventDefault();
};

const dragover = (e) => {
	e.preventDefault();
};
const indexDel = ref('');
const mouseover = (index) => {
	indexDel.value = index;
	console.log('index', index);
};
const mouseout = () => {
	indexDel.value = '';
};
const delPicHandler = () => {
	images.value.splice(indexDel.value, 1);
	// emit('change', images.value);
};

const dragend = () => {
	if (oldData !== newData) {
		let oldIndex = images.value.indexOf(oldData);
		let newIndex = images.value.indexOf(newData);

		let newItems = [...images.value];
		newItems.splice(oldIndex, 1);
		newItems.splice(newIndex, 0, oldData);
		images.value = [...newItems];
		console.log('结束的值', images.value);
		emit('change', images.value);
	}
};
const loading = ref(false);
const chooseImage = () => {
	document.getElementById('upload').click();
};
const uploadImg = (e) => {
	let file = e.target.files[0];
	let formdata = new FormData();
	formdata.append('file', file);
	uploadImage(formdata);
};
const uploadImage = (formdata) => {
	loading.value = true;
	axios.post('/Upload/uploadImage', formdata).then((res) => {
		if (!res.code) {
			loading.value = false;
			console.log('res.data', res.data);
			images.value.push({ image: res.data.key, image_url: res.data.url });
			console.log('images.value', images.value);
			emit('change', images.value);

			// 刷新列表
		} else {
			loading.value = false;
		}
	});
};
</script>

<style lang="scss" scoped>
p {
	margin: 0;
	padding: 0;
}
.drag-sort-box {
	height: 330px;
	overflow: scroll;
	width: 100%;
	display: flex;
	flex-wrap: wrap;
}
.drag-sort-box .drag-sort-item {
	width: 200px;

	margin: 10px;
	cursor: pointer;
	transition: all 0.3s;
	// background: #ccc;
	position: relative;
}

.drag-sort-box .drag-sort-item img {
	width: 100%;
	transition: all 0.3s;
	position: relative;
}
.drag-sort-box .drag-sort-item .active {
	position: absolute;
	top: 0;
	left: 0;
	align-items: center;
	justify-content: center;
	background: url(https://jira.inagora.org/secure/projectavatar?pid=11206&avatarId=10326)
		no-repeat center center;
	width: 30px;
	height: 30px;
}

.moxsind {
	width: 100%;
	height: 100%;
	position: absolute;
	top: -10px;
	left: -10px;
	z-index: 10;
	background: url(https://s5.52ritao.cn/s/70/_620652.png) no-repeat;
	background-size: 18px 18px;
	width: 18px;
	height: 18px;
}
.mg-top-wrap {
	margin-top: 10px;
}
</style>

以下是实现Vue触屏拖拽排序的步骤: 1. 使用flex布局和v-for指令来创建卡片列表。 2. 绑定触摸事件,包括touchstart、touchmove和touchend。 3. 在touchstart事件中,记录手指初始位置和卡片的初始位置。 4. 在touchmove事件中,计算手指移动的距离,并将卡片相应地移动。 5. 在touchend事件中,根据手指的最终位置和移动距离,确定卡片的最终位置。 6. 根据移动的距离和方向,判断是否需要删除或插入卡片,并更新卡片列表。 7. 完整的代码示例如下: ```html <template> <div class="card-list"> <div v-for="(card, index) in cards" :key="card.id" :style="{ transform: `translateY(${card.position}px)` }" @touchstart="onTouchStart(index)" @touchmove="onTouchMove($event, index)" @touchend="onTouchEnd(index)" > {{ card.name }} </div> </div> </template> <script> export default { data() { return { cards: [ { id: 1, name: 'Card 1', position: 0 }, { id: 2, name: 'Card 2', position: 100 }, { id: 3, name: 'Card 3', position: 200 }, ], touchStartY: 0, touchStartPos: 0, }; }, methods: { onTouchStart(index) { this.touchStartY = event.touches[0].clientY; this.touchStartPos = this.cards[index].position; }, onTouchMove(event, index) { const touchY = event.touches[0].clientY; const moveY = touchY - this.touchStartY; this.cards[index].position = this.touchStartPos + moveY; }, onTouchEnd(index) { const touchY = event.changedTouches[0].clientY; const moveY = touchY - this.touchStartY; const threshold = 50; // 移动距离阈值 if (moveY > threshold) { // 向下移动,插入到下一个位置 this.cards.splice(index + 1, 0, this.cards[index]); this.cards.splice(index, 1); } else if (moveY < -threshold) { // 向上移动,插入到上一个位置 this.cards.splice(index - 1, 0, this.cards[index]); this.cards.splice(index + 1, 1); } else { // 没有超过阈值,恢复原始位置 this.cards[index].position = this.touchStartPos; } }, }, }; </script> ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值