From 6f7ac7e5c4af6923ef50ced45531a77eb97b92f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=93=E5=B9=B3=E8=89=BA?= <52643018@qq.com> Date: Tue, 24 Aug 2021 16:10:16 +0800 Subject: [PATCH] =?UTF-8?q?[=E5=AE=8C=E5=96=84]=20=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/lf-uploadImage/lf-uploadImage.vue | 398 +++++++++++++++++-- pages/test/drag.vue | 25 +- pages/test/test.vue | 6 +- 3 files changed, 402 insertions(+), 27 deletions(-) diff --git a/components/lf-uploadImage/lf-uploadImage.vue b/components/lf-uploadImage/lf-uploadImage.vue index 1f88b0b..9769c7a 100644 --- a/components/lf-uploadImage/lf-uploadImage.vue +++ b/components/lf-uploadImage/lf-uploadImage.vue @@ -1,20 +1,40 @@ @@ -40,15 +60,76 @@ drag: { type: Boolean, // TODO 是否可拖拽排序图片 default: false + }, + padding: { + type: Number, // 图片与图片之间的间距,默认5像素,单位px + default: 6 + }, + width: { + type: String, // 每张图片的宽度,支持百分比,像素,bisection表示等分 + default: 'bisection' + }, + height: { + type: String, // 每张图片高度 + default: '222rpx' } }, data(){ return { - image_list: [] + touch: false, + startX: 0, + startY: 0, + columns: 3, + item: {}, + tranX: 0, + tranConstX: 0, + itemWrap: {}, + tranY: 0, + teanConstY: 0, + overOnePage: false, + windowHeight: 0, + originKey: null, + list: [], + cur: -1, + curZ: -1, + image_list: [], + itemWrapHeight: 0 } }, - onLoad(){ - + computed: { + itemStyle(){ + let that = this; + return function(index, item){ + let style_obj = { + height: that.$props.height + } + + let width = that.$props.width; + if(width == 'bisection'){ + width = `calc(${100 / that.columns +'%'} - 4px)` + } + style_obj.width = width; + + let str = 'translate3d('; + if(index === that.cur){ + str += that.tranX +'px'; + str += ', '; + str += that.tranY +'px'; + }else{ + str += item.tranX +'px'; + str += ', '; + str += item.tranY +'px'; + } + str += ', 0px)'; + style_obj.transform = str; + return style_obj; + } + } + }, + mounted(){ + // 'https://picsum.photos/200', + // 'https://picsum.photos/200/300' + this.init(); }, methods: { // 上传凭证图片 @@ -76,14 +157,16 @@ overstep = true; } }) + this.image_list.push(...image_list); if(overstep){ uni.showModal({ title: '温馨提示', content: '您上传的图片含有超出10M大小的限制,请优化大小后再上传!', showCancel: false }) + }else{ + this.init(); // TODO 优化每次上传都会造成重新初始化导致闪烁 } - this.image_list.push(...image_list); } }) }, @@ -99,11 +182,213 @@ }, // 移除图片 removeInage(current){ + // 移除已上传的图片 this.image_list.splice(current, 1); + // 移除定位的元素(重新初始化) + let list = this.image_list.map((item, index) => { + return { + key: index, + tranX: 0, + tranY: 0, + data: item + } + }) || []; + this.list = list; + this.getPosition(list, false); // 重新定位 + // 重新计算大盒子高度 + let rows = Math.ceil(list.length / this.columns); + let itemWrapHeight = rows * this.item.height; + itemWrapHeight += rows * this.$props.padding - this.$props.padding; + this.itemWrapHeight = itemWrapHeight; }, // 返回已上传的图片列表 getUploadImage(){ return this.image_list; + }, + // 长按触发移动排序 + longPress(e) { + this.touch = true; + this.startX = e.changedTouches[0].pageX + this.startY = e.changedTouches[0].pageY + let index = e.currentTarget.dataset.index; + if(this.columns === 1) { // 单列时候X轴初始不做位移 + this.tranConstX = 0; + } else { // 多列的时候计算X轴初始位移, 使 item 水平中心移动到点击处 + this.tranConstX = this.startX - this.item.width / 2 - this.itemWrap.left; + } + // 计算Y轴初始位移, 使 item 垂直中心移动到点击处 + this.teanConstY = this.startY - this.item.height / 2 - this.itemWrap.top; + this.tranY = this.teanConstY; + this.tranX = this.tranConstX; + this.cur = index; + this.curZ = index; + // #ifndef H5 + uni.vibrateShort(); + // #endif + }, + // 拖拽中 + touchMove(e) { + if (!this.touch) return; + let tranX = e.touches[0].pageX - this.startX + this.tranConstX; // TODO 原作者写的逻辑,但不知道为什么计算不对??? + let tranY = e.touches[0].pageY - this.startY + this.teanConstY; + let overOnePage = this.overOnePage; + + // 判断是否超过一屏幕, 超过则需要判断当前位置动态滚动page的位置 + if(overOnePage) { + if(e.touches[0].clientY > this.windowHeight - this.item.height) { + uni.pageScrollTo({ + scrollTop: e.touches[0].pageY + this.item.height - this.windowHeight, + duration: 300 + }); + } else if(e.touches[0].clientY < this.item.height) { + uni.pageScrollTo({ + scrollTop: e.touches[0].pageY - this.item.height, + duration: 300 + }); + } + } + + this.tranX = tranX; + this.tranY = tranY; + let originKey = e.currentTarget.dataset.key; + let endKey = this.calculateMoving(tranX, tranY); + + // 防止拖拽过程中发生乱序问题 + if (originKey == endKey || this.originKey == originKey) return; + this.originKey = originKey; + this.insert(originKey, endKey); + }, + // 根据当前的手指偏移量计算目标key + calculateMoving(tranX, tranY) { + let rows = Math.ceil(this.list.length / this.columns) - 1; + let i = Math.round(tranX / this.item.width); + let j = Math.round(tranY / this.item.height); + + i = i > (this.columns - 1) ? (this.columns - 1) : i; + i = i < 0 ? 0 : i; + + j = j < 0 ? 0 : j; + j = j > rows ? rows : j; + + let endKey = i + this.columns * j; + endKey = endKey >= this.list.length ? this.list.length - 1 : endKey; + return endKey + }, + // 根据起始key和目标key去重新计算每一项的新的key + insert(origin, end) { + let list; + if (origin < end) { + list = this.list.map((item) => { + if (item.key > origin && item.key <= end) { + item.key = item.key - 1; + } else if (item.key == origin) { + item.key = end; + } + return item + }); + this.getPosition(list); + } else if (origin > end) { + list = this.list.map((item) => { + if (item.key >= end && item.key < origin) { + item.key = item.key + 1; + } else if (item.key == origin) { + item.key = end; + } + return item + }); + this.getPosition(list); + } + }, + // 根据排序后 list 数据进行位移计算 + getPosition(data, vibrate = true) { + let list = data.map((item, index) => { + // X轴距离 + let tranX = this.item.width * (item.key % this.columns); + let rows = Math.ceil((item.key+1) / this.columns); + let numX = item.key; + for(let i=0; i 1){ + let numY = (rows - 1) * this.$props.padding; + tranY = tranY + numY; + } + item.tranY = tranY; + return item + }); + + this.list = list; + if(!vibrate) return; + this.itemTransition = true; + // #ifndef H5 + uni.vibrateShort(); + // #endif + + let image_list = []; + list.forEach((item) => { + image_list[item.key] = item.data + }); + + // 元素位置发生改变了,触发change事件告诉父元素更新 + this.$emit('change', {image_list: image_list}); + }, + // 拖拽结束 + touchEnd() { + if (!this.touch) return; + this.clearData(); + }, + // 清除参数 + clearData() { + this.originKey = -1; + this.touch = false; + this.cur = -1; + this.tranX = 0; + this.tranY = 0; + + // 延迟清空 + setTimeout(() => { + this.curZ = -1; + }, 300) + }, + // 初始化加载 + init() { + // 拦截image_list为空未上传图片的情况 + if(!this.image_list.length) return; + // 遍历数据源增加扩展项, 以用作排序使用 + let list = this.image_list.map((item, index) => { + return { + key: index, + tranX: 0, + tranY: 0, + data: item + } + }); + this.list = list; + this.itemTransition = false; + this.windowHeight = uni.getSystemInfoSync().windowHeight; + + setTimeout(() => { + // 获取每一项的宽高等属性 + this.createSelectorQuery().select(".item").boundingClientRect((res) => { + let rows = Math.ceil(this.list.length / this.columns); + this.item = res; + this.getPosition(this.list, false); + let itemWrapHeight = rows * res.height; // TODO 有时候会报res.height undefined + itemWrapHeight += rows * this.$props.padding - this.$props.padding; + this.itemWrapHeight = itemWrapHeight; + + this.createSelectorQuery().select(".item-wrap").boundingClientRect((res) => { + this.itemWrap = res; + let overOnePage = itemWrapHeight + res.top > this.windowHeight; + this.overOnePage = overOnePage; + }).exec(); + }).exec(); + }, 300) } } } @@ -113,14 +398,14 @@ .upload-images{ display: flex; flex-wrap: wrap; - margin-top: 30rpx; - margin-bottom: 18rpx; .upload-image-item{ - width: 220rpx; - height: 220rpx; + // width: 220rpx; + // height: 220rpx; background: #DDDDDD; position: relative; - margin-right: 12rpx; + // margin-right: 12rpx; + width: 100%; + height: 100%; &:nth-child(3n){ margin-right: 0rpx; } @@ -151,4 +436,71 @@ color: #999999; } } + + .item-wrap { + position: relative; + width: 100%; + .item { + position: absolute; + width: 100%; + z-index: 1; + &.itemTransition { + transition: transform 0.3s; + } + &.zIndex { + z-index: 2; + } + &.cur { + background: #1998FE; + transition: initial; + } + } + } + + .info { + position: relative; + // padding-top: 100%; + background: #ffffff; + // margin-right: 5px; + width: auto; + height: 100%; + &:nth-child(3n){ + margin-right: 0px; + } + & > view { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + box-sizing: border-box; + image { + width: 100%; + height: 100%; + } + } + } + + .indicator { + position: fixed; + z-index: 99999; + right: 0rpx; + top: 50%; + margin-top: -250rpx; + padding: 20rpx; + & > view { + width: 36rpx; + height: 500rpx; + background: #ffffff; + border-radius: 30rpx; + box-shadow: 0 0 10rpx -4rpx rgba(0, 0, 0, 0.5); + color: pink; + padding-top: 90rpx; + box-sizing: border-box; + font-size: 24rpx; + text-align: center; + opacity: 0.8; + } + } diff --git a/pages/test/drag.vue b/pages/test/drag.vue index 91d63e1..8182a66 100644 --- a/pages/test/drag.vue +++ b/pages/test/drag.vue @@ -1,6 +1,6 @@