功能包括:选择多张图片上传,单击预览图片,长按拖拽排序,本文主要讲解拖拽排序的实现
效果图如下:
?
实现思路:
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)"></text>
</movable-view>
<view class="wrapper" :style="{ transform: translate }">
<view class="add" @click="addImg" v-if="imgList.length < 9">
<text class="default-icon"></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重新排序。
|