uniapp 拖拽图片排序功能实现 类似于微信朋友圈效果
作者:星 辰.
这篇文章主要介绍了uniapp 拖拽图片排序功能实现类似于微信朋友圈,一部分是拖拽的放大做小,xy位置判定,图片数组的插入排序,另一部分是上传加号图片的 定位 动态计算分为几列,每一个图片大小的位置 绝对定位的计算,需要的朋友可以参考下
1、效果展示
2、一部分是拖拽的放大做小,xy位置判定,图片数组的插入排序,另一部分是上传加号图片的 定位 动态计算分为几列,每一个图片大小的位置 绝对定位的计算
<template> <view class="con"> <template v-if="viewWidth"> <movable-area class="area" :style="{ height: areaHeight }" @mouseenter="mouseenter" @mouseleave="mouseleave"> <movable-view v-for="(item, index) in imageList" :key="item.id" class="view" direction="all" :y="item.y" :x="item.x" :damping="40" :disabled="item.disable" @change="onChange($event, item)" @touchstart="touchstart(item)" @mousedown="touchstart(item)" @touchend="touchend(item)" @mouseup="touchend(item)" :style="{ width: viewWidth + 'px', height: viewWidth + 'px', 'z-index': item.zIndex, opacity: item.opacity }"> <view class="area-con" :style="{ width: childWidth, height: childWidth, borderRadius: borderRadius + 'rpx', transform: 'scale(' + item.scale + ')' }"> <image class="pre-image" :src="item.src" mode="aspectFill"></image> <view class="del-con" @click="delImages(item, index)" @touchstart.stop="delImageMp(item, index)" @touchend.stop="nothing()" @mousedown.stop="nothing()" @mouseup.stop="nothing()"> <view class="del-wrap"> <image class="del-image" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyBjbGFzcz0iaWNvbiIgd2lkdGg9IjIwMHB4IiBoZWlnaHQ9IjIwMC4wMHB4IiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTYyNS40MjUzMDYgNjgxLjU4OTQ2NmwtMTE1LjQ3Mjk0MS0xMTUuNDcyOTQxLTExNS40NzE5MTggMTE1LjQ3Mjk0MS01NC4xMTc1NDgtNTQuMTE4NTcyIDExNS40NzE5MTgtMTE1LjQ3MTkxOC0xMTUuNDcxOTE4LTExNS40NzI5NDEgNTQuMTE3NTQ4LTU0LjExNzU0OCAxMTUuNDcxOTE4IDExNS40NzE5MTggMTE1LjQ3Mjk0MS0xMTUuNDcxOTE4IDU0LjExNzU0OCA1NC4xMTc1NDgtMTE1LjQ3Mjk0MSAxMTUuNDcyOTQxIDExNS40NzI5NDEgMTE1LjQ3MTkxOEw2MjUuNDI1MzA2IDY4MS41ODk0NjZ6TTc4MC41NDMxNzYgMjQxLjQwOTE4OWMtMTQ4LjgyNDUzNy0xNDguODI0NTM3LTM5Mi4zNTYwNjMtMTQ4LjgyNDUzNy01NDEuMTgwNiAwcy0xNDguODI0NTM3IDM5Mi4zNTUwMzkgMCA1NDEuMTc5NTc2IDM5Mi4zNTYwNjMgMTQ4LjgyNDUzNyA1NDEuMTgwNiAwQzkyOS4zNjc3MTMgNjMzLjc2NTI1MSA5MjkuMzY3NzEzIDM5MC4yMzM3MjYgNzgwLjU0MzE3NiAyNDEuNDA5MTg5eiIgZmlsbD0iI2RiZGJkYiIgLz48L3N2Zz4="> </image> </view> </view> </view> </movable-view> <view class="add" v-if="imageList.length < number" :style="{ top: add.y, left: add.x, width: viewWidth + 'px', height: viewWidth + 'px' }" @click="addImages"> <view class="add-wrap" :style="{ width: childWidth, height: childWidth, borderRadius: borderRadius + 'rpx' }"> + </view> </view> </movable-area> </template> </view> </template> <script> export default { emits: ['input', 'update:modelValue'], props: { // 排序图片 value: { type: Array, default: function() { return [] } }, // 排序图片 modelValue: { type: Array, default: function() { return [] } }, // 从 list 元素对象中读取的键名 keyName: { type: String, default: null }, // 选择图片数量限制 number: { type: Number, default: 9 }, // 图片父容器宽度(实际显示的图片宽度为 imageWidth / 1.1 ),单位 rpx // imageWidth > 0 则 cols 无效 imageWidth: { type: Number, default: 0 }, // 图片列数 cols: { type: Number, default: 3 }, // 图片圆角,单位 rpx borderRadius: { type: Number, default: 10 }, // 图片周围空白填充,单位 rpx padding: { type: Number, default: 10 }, // 拖动图片时放大倍数 [0, ∞) scale: { type: Number, default: 1.1 }, // 拖动图片时不透明度 opacity: { type: Number, default: 0.7 }, // 自定义添加 addImage: { type: Function, default: null }, // 删除确认 delImage: { type: Function, default: null } }, data() { return { imageList: [], width: 0, add: { x: 0, y: 0 }, colsValue: 0, viewWidth: 0, tempItem: null, timer: null, changeStatus: true, preStatus: true, first: true, } }, computed: { areaHeight() { let height = '' // return '355px' if (this.imageList.length < this.number) { height = (Math.ceil((this.imageList.length + 1) / this.colsValue) * this.viewWidth).toFixed() + 'px' } else { height = (Math.ceil(this.imageList.length / this.colsValue) * this.viewWidth).toFixed() + 'px' } console.log('areaHeight', height) return height }, childWidth() { return this.viewWidth - this.rpx2px(this.padding) * 2 + 'px' }, }, watch: { value: { handler(n) { if (!this.first && this.changeStatus) { console.log('watch', n) let flag = false for (let i = 0; i < n.length; i++) { if (flag) { this.addProperties(this.getSrc(n[i])) continue } if (this.imageList.length === i || this.imageList[i].src !== this.getSrc(n[i])) { flag = true this.imageList.splice(i) this.addProperties(this.getSrc(n[i])) } } } }, deep: true }, modelValue: { handler(n) { if (!this.first && this.changeStatus) { console.log('watch', n) let flag = false for (let i = 0; i < n.length; i++) { if (flag) { this.addProperties(this.getSrc(n[i])) continue } if (this.imageList.length === i || this.imageList[i].src !== this.getSrc(n[i])) { flag = true this.imageList.splice(i) this.addProperties(this.getSrc(n[i])) } } } }, deep: true }, }, created() { // 获取设备宽度 this.width = uni.getSystemInfoSync().windowWidth }, mounted() { // 获取当前的存放移动区域的属性 const query = uni.createSelectorQuery().in(this) query.select('.con').boundingClientRect(data => { // 设置的三列 进行传值 this.colsValue = this.cols // 元素宽度除以三进行均分 this.viewWidth = data.width / this.cols if (this.imageWidth > 0) { this.viewWidth = this.rpx2px(this.imageWidth) this.colsValue = Math.floor(data.width / this.viewWidth) } let list = this.value // #ifdef VUE3 list = this.modelValue // #endif for (let item of list) { this.addProperties(this.getSrc(item)) } this.first = false }) query.exec() }, methods: { // 传的如果有键值就采取这个方法。没有就不需要 getSrc(item) { if (this.keyName !== null) { return item[this.keyName] } return item }, onChange(e, item) { if (!item) return item.oldX = e.detail.x item.oldY = e.detail.y // 如果是拖动状态中 if (e.detail.source === 'touch') { if (item.moveEnd) { item.offset = Math.sqrt(Math.pow(item.oldX - item.absX * this.viewWidth, 2) + Math.pow(item.oldY - item .absY * this.viewWidth, 2)) } // x为 移动时候的坐标点加上划定的一半的区域的和 除以划定区域 去判断有没有超过自己定义的最大列数 let x = Math.floor((e.detail.x + this.viewWidth / 2) / this.viewWidth) if (x >= this.colsValue) return // y同理 也是判断超过高度没有 let y = Math.floor((e.detail.y + this.viewWidth / 2) / this.viewWidth) // index则是 如果是第一张图片右移动了半张图片的位置 index就会加一 let index = this.colsValue * y + x if (item.index != index && index < this.imageList.length) { this.changeStatus = false for (let obj of this.imageList) { // 判断图片是左右上下移动 因为这个函数是一直触发 if (item.index > index && obj.index >= index && obj.index < item.index) { this.change(obj, 1) } else if (item.index < index && obj.index <= index && obj.index > item.index) { this.change(obj, -1) } else if (obj.id != item.id) { obj.offset = 0 obj.x = obj.oldX obj.y = obj.oldY setTimeout(() => { this.$nextTick(() => { obj.x = obj.absX * this.viewWidth obj.y = obj.absY * this.viewWidth }) }, 0) } } item.index = index item.absX = x item.absY = y if (!item.moveEnd) { setTimeout(() => { this.$nextTick(() => { item.x = item.absX * this.viewWidth item.y = item.absY * this.viewWidth }) }, 0) } // console.log('bbb', JSON.parse(JSON.stringify(item))); // 移动完成后重新排序 this.sortList() } } }, // change事件会随着移动函数一直触发,index随之变化修改图片的位置xy change(obj, i) { obj.index += i obj.offset = 0 obj.x = obj.oldX obj.y = obj.oldY obj.absX = obj.index % this.colsValue obj.absY = Math.floor(obj.index / this.colsValue) setTimeout(() => { this.$nextTick(() => { obj.x = obj.absX * this.viewWidth obj.y = obj.absY * this.viewWidth }) }, 0) }, // 长按图片时候进行所有的层级进行加大 和放大 touchstart(item) { this.imageList.forEach(v => { v.zIndex = v.index + 9 }) item.zIndex = 99 item.moveEnd = true this.tempItem = item this.timer = setTimeout(() => { item.scale = this.scale item.opacity = this.opacity clearTimeout(this.timer) this.timer = null }, 200) }, // 点击一次没有触发四个变量的更改就会触发previewImage 变成预览 // 拖拽过程中几个变量会变,就不会触发预览 拖拽结束后就会缩小并且改变位置 touchend(item) { this.previewImage(item) item.scale = 1 item.opacity = 1 item.x = item.oldX item.y = item.oldY item.offset = 0 item.moveEnd = false setTimeout(() => { this.$nextTick(() => { item.x = item.absX * this.viewWidth item.y = item.absY * this.viewWidth this.tempItem = null this.changeStatus = true }) // console.log('ccc', JSON.parse(JSON.stringify(item))); }, 0) // console.log('ddd', JSON.parse(JSON.stringify(item))); }, previewImage(item) { // timer是定时器 changeStatus是不是在移动状态中 只要点击移动了就是false offset也是为0 if (this.timer && this.preStatus && this.changeStatus && item.offset < 28.28) { clearTimeout(this.timer) this.timer = null const list = this.value || this.modelValue let srcList = list.map(v => this.getSrc(v)) console.log(list, srcList); uni.previewImage({ urls: srcList, current: item.src, success: () => { this.preStatus = false setTimeout(() => { this.preStatus = true }, 600) }, fail: (e) => { console.log(e); } }) } else if (this.timer) { clearTimeout(this.timer) this.timer = null } }, mouseenter() { //#ifdef H5 this.imageList.forEach(v => { v.disable = false }) //#endif }, mouseleave() { //#ifdef H5 if (this.tempItem) { this.imageList.forEach(v => { v.disable = true v.zIndex = v.index + 9 v.offset = 0 v.moveEnd = false if (v.id == this.tempItem.id) { if (this.timer) { clearTimeout(this.timer) this.timer = null } v.scale = 1 v.opacity = 1 v.x = v.oldX v.y = v.oldY this.$nextTick(() => { v.x = v.absX * this.viewWidth v.y = v.absY * this.viewWidth this.tempItem = null }) } }) this.changeStatus = true } //#endif }, addImages() { if (typeof this.addImage === 'function') { this.addImage.bind(this.$parent)() } else { let checkNumber = this.number - this.imageList.length uni.chooseImage({ count: checkNumber, sourceType: ['album', 'camera'], success: res => { let count = checkNumber <= res.tempFilePaths.length ? checkNumber : res .tempFilePaths.length for (let i = 0; i < count; i++) { this.addProperties(res.tempFilePaths[i]) } this.sortList() } }) } }, delImages(item, index) { if (typeof this.delImage === 'function') { this.delImage.bind(this.$parent)(() => { this.delImageHandle(item, index) }) } else { this.delImageHandle(item, index) } }, delImageHandle(item, index) { this.imageList.splice(index, 1) for (let obj of this.imageList) { if (obj.index > item.index) { obj.index -= 1 obj.x = obj.oldX obj.y = obj.oldY obj.absX = obj.index % this.colsValue obj.absY = Math.floor(obj.index / this.colsValue) this.$nextTick(() => { obj.x = obj.absX * this.viewWidth obj.y = obj.absY * this.viewWidth }) } } this.add.x = (this.imageList.length % this.colsValue) * this.viewWidth + 'px' this.add.y = Math.floor(this.imageList.length / this.colsValue) * this.viewWidth + 'px' this.sortList() }, delImageMp(item, index) { //#ifdef MP this.delImages(item, index) //#endif }, sortList() { console.log('sortList'); const result = [] let source = this.value // #ifdef VUE3 source = this.modelValue // #endif // 使用slice进行深拷贝 let list = this.imageList.slice() // 小到大排序 list.sort((a, b) => { return a.index - b.index }) for (let s of list) { let item = source.find(d => this.getSrc(d) == s.src) if (item) { result.push(item) } else { if (this.keyName !== null) { result.push({ [this.keyName]: s.src }) } else { result.push(s.src) } } } this.$emit("input", result); this.$emit("update:modelValue", result); }, addProperties(item) { console.log(item); // 这里的数组长度还没有。计算后push进去才从0开始计算 // 数组长度取列数的余数 1就取1 2就取2 3就是0 4就是1 let absX = this.imageList.length % this.colsValue // 向下取整数组长度除以列数 1/3取0 4/3取1 let absY = Math.floor(this.imageList.length / this.colsValue) let x = absX * this.viewWidth let y = absY * this.viewWidth this.imageList.push({ src: item, x, y, oldX: x, oldY: y, absX, absY, scale: 1, zIndex: 9, opacity: 1, index: this.imageList.length, id: this.guid(16), disable: false, offset: 0, moveEnd: false }) this.add.x = (this.imageList.length % this.colsValue) * this.viewWidth + 'px' this.add.y = Math.floor(this.imageList.length / this.colsValue) * this.viewWidth + 'px' }, nothing() {}, rpx2px(v) { return this.width * v / 750 }, guid(len = 32) { const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('') const uuid = [] const radix = chars.length for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix] uuid.shift() return `u${uuid.join('')}` } } } </script> <style lang="scss" scoped> .con { // padding: 30rpx; .area { width: 100%; .view { display: flex; justify-content: center; align-items: center; .area-con { position: relative; overflow: hidden; .pre-image { width: 100%; height: 100%; } .del-con { position: absolute; top: 0rpx; right: 0rpx; padding: 0 0 20rpx 20rpx; .del-wrap { width: 40rpx; height: 40rpx; // background-color: rgba(0, 0, 0, 0.4); border-radius: 0 0 0 10rpx; display: flex; justify-content: center; align-items: center; .del-image { width: 40rpx; height: 40rpx; } } } } } .add { position: absolute; display: flex; justify-content: center; align-items: center; .add-wrap { display: flex; justify-content: center; align-items: center; border: 1rpx solid #000; // background-color: #eeeeee; } } } } </style>
到此这篇关于uniapp 拖拽图片排序, 类似于微信朋友圈的文章就介绍到这了,更多相关uniapp 拖拽图片排序内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!