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

效果图如下:

 

实现思路:

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重新排序。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐