触发顺序及次数

被拖拽元素,事件触发顺序是 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)" -->
			class="drag-sort-box"
				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()"
					:class="index === indexDel ? 'moxsind' : ''"
					@click="delPicHandler()"
				<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>
	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>
                                    下面是HTML5的拖拽事件
dragstart:开始拖元素触发,作用于拖拽元素
dragenter:元素拖进可drop元素(绑定drop事件的元素)时触发,作用于目标元素
dragover:当元素拖动到drop元素上时触发,作用于目标元素
drop:当元素放下到drop元素触发,作用于目标元素
dragleave :当元素离开drop元素时触发,作用于目标元素
drag:每次元素被拖动时会触发,作用于目标元素
dragend:放开拖动元素时触发,作用于目标元素
完成一次拖放的事件过程是: dragstart
                                    今天的文章可谓是积蓄了我这几年来的应聘和面试经历总结出来的经验,干货满满呀!如果你能够一直坚持看到这儿,那么首先我还是十分佩服你的毅力的。所以看完之后,还是多多行动起来吧!可以非常负责地说,如果你能够坚持把我上面列举的内容都一个不拉地看完并且全部消化为自己的知识的话,那么你就至少已经达到了中级开发工程师以上的水平,进入大厂技术这块是基本没有什么问题的了。开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
                                    事件:当拖拽元素开始被拖拽的时候触发的事件,此事件作用在被拖曳元素上事件:当拖拽完成后触发的事件,此事件作用在被拖曳元素上事件:当拖曳元素进入目标元素的时候触发的事件,此事件作用在目标元素上事件:拖拽元素在目标元素上移动的时候触发的事件,此事件作用在目标元素上事件:拖拽元素在目标元素上移动的时候触发的事件,此事件作用在目标元素上事件:被拖拽的元素在目标元素上同时鼠标放开触发的事件,此事件作用在目标元素上方法:阻止默认的些事件方法等执行。
dataTransfer:
dataTransfer 对象允许我们在开始拖动元素时设置数据,并在将元素放在拖放区中时访问相同的数据。
我们应该知道一些关于 dataTransfer 的属性和方法(如果要了解更多,请查看dataTransfer API 文档)。
dropEffect:当前的拖放操作(例如,移动,复制)
effectAllowed:指定拖
                                    因为项目有rem适配,使用第三方插件无法处理适配问题,所有只能自己写拖拽功能了拖拽一般都会想到按下,移动,放开,但是本人亲测,就在div绑定一个按下事件就行了(在事件里面写另外两个事件),另外两个绑上,会莫名其妙卡死,那种莫名其妙的问题推荐几个开发调试时使用的第三方拖动插件吧,虽然没用上,但是他们是真的好和,其中前者更轻量化,后者功能更全。
                                    draggable属性是 HTML5 新增的可拖拽属性HTML 中,除了图像、链接和选择的文本默认可拖拽外,其他元素默认是不可拖拽的。如果想让其他元素变成可拖拽的,首先需要把draggable属性设置为。
                                    是一个Vue.js的拖放和排序列表组件。它基于库,可以用于实现拖放、排序、带有动画效果的过渡以及其他特性。支持Vue.js 2.x和3.x,可以在任何现代浏览器和移动设备上使用。还支持自定义拖放元素,可以用于实现更复杂的拖放和排序功能。下面是一个例子:return {return {} .clone {} .drag {} .text {flex : 1;