uniapp使用movable-view容器实现九宫格拖拽排序功能

发布时间:2022-04-24 14:23

功能包括:选择多张图片上传,单击预览图片,长按拖拽排序,本文主要讲解拖拽排序的实现

效果图如下:

图片[1] - uniapp使用movable-view容器实现九宫格拖拽排序功能 - 尘心网

实现思路:

1.定义imgList数组,存放图片元素;

2.长按图片时记录当前移动元素index,简称moveId;

3.移动时,记录结束位置,计算结束位置的index,简称moveToId;

4.移动结束,记录原来的元素信息imgList[moveId],使用splice方法删除moveId元素,添加moveToId元素

5.利用归位函数为movable-view的x、y赋值,将元素归位

代码片段:

<template>
	<movable-area class="img-container">
		<movable-view
			class="wrapper"
			v-for="(item, index) in imgList"
			:key="index"
			:x="item.x"
			:y="item.y"
			direction="all"
			:animation="false"
			:disabled="!isMove"
			@change="moveStatus"
			@longpress="moveStart"
			@touchend="moveEnd"
			:data-moveid="index"
			:style="{ zIndex: index == moveId ? 2 : 1 }"
		>
			<image
				class="image"
				mode="aspectFill"
				:src="item.img"
				@click="preview(item)"
				:class="{ active: index == moveId, shadow: index == moveToId }"
				@load="hideLoading"
			/>
			<text class="close-icon" @click="deletePic(index)">&#xe6e8;</text>
		</movable-view>
		<view class="wrapper" :style="{ transform: translate }">
			<view class="add" @click="addImg" v-if="imgList.length < 9">
				<text class="default-icon">&#xe71b;</text>
				<text class="tips">点击上传图片</text>
			</view>
		</view>
	</movable-area>
</template>
// data
isMove: boolean = false;
moveId: number = -1; //移动的是哪个元素块
moveToId: number = -1; //移动到是哪个元素块
endX: number = 0; //最终停止的位置
endY: number = 0;
imgList: any[] = [
// {
// 	img: '',
// 	x: 0,
// 	y: 0
// },
];
...
// 下方为主要的js逻辑
moveStart(e) {
// 注意:点击预览图片时会触发moveEnd方法,使用isMove用来判断是否可移动
this.isMove = true;
// 记录移动元素的index
this.moveId = e.currentTarget.dataset.moveid;
// 初始化moveToId
this.moveToId = this.moveId;
}
moveStatus(e) {
//移动的块ID
if (e.detail.source == 'touch') {
//最终坐标
this.endX = e.detail.x;
this.endY = e.detail.y;
//计算移动结束的index值
let range:number = this.deviceWidth*110/375;
let x:number = Math.floor(this.endY / range);
let y:number = Math.floor(this.endX / range);
this.moveToId =x * 3 + y
}
}
moveEnd(e) {
if (!this.isMove) {
// 点击预览时不作为
return false;
}
let newList:any = this.deepCopy(this.imgList);
// 重新排序imgList
// 注意,下方步骤是将移动后的坐标赋给imgList,避免重新归位的时候dom不更新
if (this.moveId == this.moveToId) {
// 如果未改变位置
this.$set(this.imgList[this.moveId], 'x', this.endX*2+'rpx');
this.$set(this.imgList[this.moveId], 'y', this.endY*2+'rpx');
} else {
let obj: any = JSON.parse(JSON.stringify(this.imgList[this.moveId]));
newList.splice(this.moveId, 1);
newList.splice(this.moveToId, 0, obj);
// 更新dom
newList.forEach((item, i) => {
this.$set(this.imgList[i], 'img', item.img);
this.$set(this.imgList[i], 'x', this.endX*2+'rpx');
this.$set(this.imgList[i], 'y', this.endY*2+'rpx');
});
}
// 将重新排序的数组归坑
this.$nextTick(function() {
setTimeout(() => {
this.initMove();
}, 100);
});
}
initMove() {
// 将九张图片按顺序归位(移动,添加,删除)
let list:any = this.deepCopy(this.imgList);
list.forEach((item, index) => {
let row = Math.ceil((index + 1) / 3);
let col = index % 3;
this.$set(this.imgList[index], 'x', 230 * col+'rpx');
this.$set(this.imgList[index], 'y', 230 * (row - 1)+'rpx');
});
// 样式回归(选中的透明度和移动结束位置的遮罩样式根据moveId和moveToId变化)
this.moveId = -1;
this.moveToId = -1;
this.isMove = false;
}
deepCopy(obj) {
// 深拷贝对象
if (typeof obj !== 'object') return;
// 根据obj的类型判断是新建一个数组还是一个对象
let newObj = obj instanceof Array ? [] : {};
for (let key in obj) {
// 遍历obj,并且判断是obj的属性才拷贝
if (obj.hasOwnProperty(key)) {
// 判断属性值的类型,如果是对象递归调用深拷贝
newObj[key] = typeof obj[key] === 'object' ? this.deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}

注意问题:

1.移动后执行归位函数,dom不更新

官方解释查看

原因:当重复设置某些属性为相同的值时,不会同步到view层。 每次将movable-view组件的x和y属性值设置为相同的值,只有第一次能顺利归位。 这和props的单向数据流特性有关,组件内部x、y的实际值改动后,其绑定的属性并不会一同变化。

解决方案:在moveStatus中记录移动值,在结束时赋给移动元素的x和y,dom更新后赋予归位数值。

2.另一种排序思路

移动结束后,将endX和endY赋给imgList[moveId],然后定义sort排序方法,将imgList重新排序。

文档下载:uniapp使用movable-view容器实现九宫格拖拽排序功能.doc文档

THE END
喜欢就支持一下吧