当前位置: 首页 > news >正文

uni-app实现小程序、H5图片轮播预览、双指缩放、双击放大、单击还原、滑动切换功能

前言

这次的标题有点长,主要是想要表述的功能点有点多;

简单做一下需求描述

产品要求在商品详情页的头部轮播图部分,可以单击预览大图,同时在预览界面可以双指放大缩小图片并且可以移动查看图片,双击放大,单击还原,左右滑动可以切换预览的图片,非放大情况下单击退出预览(类似于淘宝现在的商详图片预览);要求微信小程序和H5中都实现该功能,时间1.5天;

需求分析

  • 轮播图片点击唤起预览界面(这部分功能已经很早实现了,不做过多的解释),界面中可以定制别的内容;
  • 预览图片双指缩放
  • 预览图片放大之后可以拖动查看图片
  • 双击放大
  • 单击还原
  • 滑动切换图片
  • 单击关闭预览图片,同时索引定位到预览的位置

简单思路

图片点击预览图片这个功能是之前就有的,这次其实是加入了放大缩小手势等,想着直接用uni-app的uni.previewImage它支持图片预览,双击放大,拖动,轮播,而且底层是native的性能很棒,很丝滑;不支持关闭预览定位索引,不支持预览界面定制别的内容,因此没办法直接放弃了;

于是打算原生手写一个,尝试之后发现H5能用,但是很卡顿,小程序没法看;

最后想到了可以用uni-app的movable-area和movable-view,开发一个可以拖动的区域,配合swiper就可以了;正好看了一下uni.previewImage的实现源码,发现在H5端也是用这几个组件实现的源码位置,于是决定参照源码开发一下;

代码

<div :class="['img-preview', modal ? 'slide-down-to-up-opacity' : 'slide-up-to-down-opacity']"><swiper class="swiper-container" :current="current" :disable-touch="disableTouch" @change="handleChangeSlide"><swiper-item v-for="(img, idx) in picList" :key="idx" :class="{'swiper-slide': true}"><movable-area scale-area class="movable-area"><movable-viewdirection="all":animation="false":scale-min="1":scale-max="2":damping="30":scale-value="img.scale":scale="true":inertia="false":out-of-bounds="false":class="{'movable-view':true}"@touchmove="handleTouchmove($event, idx)"@click.stop="handleMovableClick($event, idx)"@scale.stop="handleOnScale($event, idx)"><img:key="award ? img.productImageSpecial : img.picture":src="award ? img.productImageSpecial : img.picture"mode="widthFix":class="{'preview-img': true}"/></movable-view></movable-area> </swiper-item></swiper><div v-if="picList && picList.length > 1" class="product-align-single"><div class="product-align-dots"><div v-for="(item, idx) in picList" :key="idx" :class="{'product-align-dot': true, 'product-align-dot-active': idx === current}"></div></div></div></div>
export default {name: 'ImgPreview',props: {// 显示与隐藏value: {type: Boolean,value: false},imgList: {type: Array,default() {return []}},initIndex: {type: Number,default: 0},fullscreen: {type: Boolean,default: true},award: {type: Boolean,default: false}},emits:['close','change-slide'],data () {return {modal: this.value,current: this.initIndex,arrowIcon: 'https://static1.keepcdn.com/infra-cms/2023/3/7/17/35/553246736447566b58312f38753731477849327742542f44796c385238397273617968664475477a4f6c4d3d/48x48_e33efe885c6a5df9403962315de3681bad220cd2.png',scale: 1,lastTapTime: 0, // 记录上一次点击时间clickTimer: null,clickDelay: 300,disableTouch: false,picList: []}},watch: {value: {handler(val) {this.modal = valif (val) {this.picList = []this.imgList.forEach(item => {this.picList.push({...item,scale: 1})})}},immediate:true},},methods: {handleOnScale(event, index) {const { scale, x, y } = event.detaillet item = this.picList[index]item.scale = scalethis.$set(this.picList, index, item)this.$forceUpdate()},handleTouchmove(event, index) {this.disableTouch = truelet item = this.picList[index]if (item.scale !== 1) {this.disableTouch = true} else {this.disableTouch = false}},handleMovableClick(e, index) {console.log(e, '<===========================')// 判断双击事件let curTime = e.timeStampif (this.lastTapTime > 0) {if (curTime - this.lastTapTime < this.clickDelay) {this.lastTapTime = curTimeclearTimeout(this.clickTimer)// 双击return this.handleMovableDbClick(e, index)}}this.lastTapTime = curTime;clearTimeout(this.clickTimer);this.clickTimer = setTimeout(() => {// 单击this.handleMovableOnClick(e, index)}, this.clickDelay)},// 图片单击事件(关闭预览)handleMovableOnClick(e, index) {this.modal = falsesetTimeout(() => {this.$emit('close', false)}, 100)},// 图片双击事件handleMovableDbClick(e, index) {let item = this.picList[index]item.scale = item.scale < 2 ? 2 : 1this.$set(this.picList, index, item)this.$forceUpdate()},handleChangeSlide(event) {this.current = event.detail?.current || 0this.$emit('change-slide', this.current)this.resetScale(this.current)},resetScale(index) {this.picList.forEach((element, idx) => {if (idx !== index) {element.scale = 1}})this.$forceUpdate()}}
}
<style lang="less" scoped>
.img-preview {position: fixed;top: 0;left: 0;right: 0;bottom: 0;z-index: 9999;opacity: 0;
}
.img-preview-bg {position: absolute;top: 0;left: 0;right: 0;bottom: 0;background: rgba(0, 0, 0, 1);z-index: 1;
}
.movable-area {height: 100%;width: 100%;overflow: hidden;
}
.movable-view {height: 100%;width: 100%;
}.img-preview-bg {position: absolute;top: 0;left: 0;right: 0;bottom: 0;background: rgba(0, 0, 0, 1);z-index: 1;
}
.preivew-swiper{width: 100%;height: 100%;display: flex;flex-direction: column;align-items: center;box-sizing: border-box;// padding-top: calc((100vh - 100vw) * 0.356);position: relative;z-index: 2;
}
.preivew-swiper-fullscreen {padding-top: calc((100vh - 100vw) * 0.5);
}
.swiper-container {width: 100%;height: 100%;box-sizing: border-box;display: flex;flex-direction: column;align-items: center;position: relative;z-index: 2;
}
.swiper-wrapper,
.swiper-slide {width: 100% !important;height: 100%;display: flex;align-items: center;
}
.swiper-slide-single {height: 133.34vw;
}
.preview-img {width: 100%;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);max-height: 100%;max-width: 100%;
}
</style>

css不太全,截取了一部分;

主要是movable-view组件的一些属性配置和事件触发;这里有一点需要注意就是在图片放大的情况下移动图片或触发swiper的滑动,这里就出现了一个问题我搞了半天但是还是没有解决;

怎么阻止swiper手动切换

阻止冒泡事件,event.stopPropagation();

uniapp中禁止 event.preventDefault();event.stopPropagation();
在这里插入图片描述
要想阻止冒泡事件只能用事件修饰符;显然事件修饰符不能根据条件修改,这个路不通;

swiper有没有什么可以禁止滑动的属性呢?有的!
disable-touch

查了一下swiper果然有个属性disable-touch;很开心,终于可以根据条件阻止swiper滑动了,当在movable-view中touchmove且scale!==1的时候disable-touch设为true,反之为false;
但是当在小程序中测试时,发现这个属性并不管用,后来发现该属性在小程序中只有初始化时有用,不能做到动态变更;
在这里插入图片描述

swiper-item添加touchmove

网上很火的解决方案都在21年左右的,但是尝试了一下行不通,不好用!

写一个伪类,用一个蒙层盖住swiper
.swiper {position: relative;&:after {content: '';position: absolute;top: 0;left: 0;right: 0;bottom: 0;z-index: 2;}
}

这个方法很好用,之前在别的需求中用过,盖住之后拖动肯定就不滑动了,但是现在的需求显然不能这么用,因为movable-view在swiper中需要拖动;

最后效果

小程序的swiper阻止切换没有实现,同时该组件在小程序端明显卡段,动画不流畅,也没有native那种回弹的效果,跟产品商量了一下也对比了一下决定来个AB实验;

  • 小程序端直接用uni.previewImageAPI,毕竟用户就是想放大看看图片,没必要做那么多嵌套,动画流畅,体验敢强最重要;至于关闭定位索引和在弹框slot别的内容这些暂时在小程序端先不做;
  • H5端用自己写的组件如上,因为uni.previewImage在H5端的效果一般,并且不能双击放大,其余的动画流畅度和性能都一样;
  • 暂时先这样了,也没有过多的人力去研究这个H5的动画,也没必要做个引擎之类的;

参考

  • 移动端单指移动和双指缩放的实现
  • uniapp(移动端)图片双指缩放、单指拖动、双击缩放
  • dcloudio/uni-app
  • uniapp使用 movable-area movable-view 实现图片双指缩放、鼠标单击缩小双击放大、图片及标记点功能
  • 微信小程序swiper禁止用户滑动
  • 苛学加/previewImage

如果有需要增加图片旋转或者长按事件等可以参考这个,可以结合一下看看;就到这里吧;预览图有同学需要可以找我要,我看见就会回复!拜拜~~~


http://www.mrgr.cn/news/80015.html

相关文章:

  • 去除背景 学习笔记
  • 2020-08-05 如何学习数据结构与算法
  • mp4影像和m4a音频无损合成视频方法
  • Maven学习(依赖版本维护、依赖传递、解决Maven依赖冲突的3种方式)
  • C++是如何工作的?
  • 为什么数据平台需要敏捷版|直播回顾
  • 【数据结构进阶】AVL树深度剖析 + 实现(附源码)
  • 【电机控制器】FM33LF015芯片——FLASH模拟EEPROM
  • 黑皮书-计算机科学导论02
  • react-dnd 拖拽事件与输入框的文本选中冲突
  • Opencv之图像添加水印
  • HCIA-Access V2.5_2_2网络通信基础_TCP/IP协议栈报文封装
  • SAP FICO物料分类账
  • 任务4 DNS服务配置与管理
  • ubuntu22.04 使用crash
  • SpringBoot 手动实现动态切换数据源 DynamicSource (中)
  • 【大前端vue:组件】鼠标上移 出现动画
  • pyfink1.20版本下实现消费kafka中数据并实时计算
  • 【架构】从 Socket 的角度认识非阻塞模型
  • xshell连接虚拟机,更换网络模式:NAT->桥接模式
  • 网络基础 - TCP/IP 五层模型
  • 爬虫基础知识点
  • 设计模式——Singleton(单例)设计模式
  • 12.12 深度学习-注意力机制
  • Java从入门到工作3 - 框架/工具
  • 如何在项目中使用人大金仓替换mysql