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
如果有需要增加图片旋转或者长按事件等可以参考这个,可以结合一下看看;就到这里吧;预览图有同学需要可以找我要,我看见就会回复!拜拜~~~
相关文章:
uni-app实现小程序、H5图片轮播预览、双指缩放、双击放大、单击还原、滑动切换功能
前言 这次的标题有点长,主要是想要表述的功能点有点多; 简单做一下需求描述 产品要求在商品详情页的头部轮播图部分,可以单击预览大图,同时在预览界面可以双指放大缩小图片并且可以移动查看图片,双击放大࿰…...
游戏引擎学习第45天
仓库: https://gitee.com/mrxiao_com/2d_game 回顾 我们刚刚开始研究运动方程,展示了如何处理当人物遇到障碍物时的情况。有一种版本是角色会从障碍物上反弹,而另一版本是角色会完全停下来。这种方式感觉不太自然,因为在游戏中,…...
electron常用方法
一,,electron设置去除顶部导航栏和menu 1,electron项目 在创建BrowserWindow实例的main.js页面添加frame:false属性 2,electron-vue项目 在src/main/index.js文件下找到创建窗口的方法(createWindow)&…...
【Spark】Spark Join类型及Join实现方式
如果觉得这篇文章对您有帮助,别忘了点赞、分享或关注哦!您的一点小小支持,不仅能帮助更多人找到有价值的内容,还能鼓励我持续分享更多精彩的技术文章。感谢您的支持,让我们一起在技术的世界中不断进步! Sp…...
meta llama 大模型一个基础语言模型的集合
LLaMA 是一个基础语言模型的集合,参数范围从 7B 到 65B。我们在数万亿个 Token 上训练我们的模型,并表明可以专门使用公开可用的数据集来训练最先进的模型,而无需诉诸专有的和无法访问的数据集。特别是,LLaMA-13B 在大多数基准测试…...
JAVA爬虫获取1688关键词接口
以下是使用Java爬虫获取1688关键词接口的详细步骤和示例代码: 一、获取API接口访问权限 要使用1688关键词接口,首先需要获取API的使用权限,并了解接口规范。以下是获取API接口的详细步骤: 注册账号:在1688平台注册一…...
操作系统——内存管理
1、什么是虚拟内存?它是如何实现的?虚拟内存与物理内存之间有什么关系? 虚拟内存是操作系统提供的一种内存管理机制,它使程序认为自己拥有连续的内存空间,但实际上内存可能被分散存储在物理内存和磁盘交换空间中。 虚…...
android studio 模拟器不能联网?
模拟器路径: C:\Users\Administrator\AppData\Local\Android\Sdk\emulator\emulator.exe.关闭所有AVD设备实例 导航至: C:\Users\userName\AppData\Local\Android\Sdk\emulator查看模拟器名称 AdministratorDESKTOP-6JB1OGC MINGW64 ~/AppData/Local/…...
CTF-WEB: 目录穿越与模板注入 [第一届国城杯 Ez_Gallery ] 赛后学习笔记
step1 验证码处存在逻辑漏洞,只要不申请刷新验证码就一直有效 字典爆破得到 admin:123456 step2 /info?file../../../proc/self/cmdline获得 python/app/app.py经尝试,读取存在的目录时会返回 A server error occurred. Please contact the administrator./info?file.…...
数据结构6.4——归并排序
基本思想: 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个…...
【html 常用MIME类型列表】
本表仅列出了常用的MIME类型,完整列表参考文档。 浏览器通常使用 MIME 类型(而不是文件扩展名)来确定如何处理 URL,因此 Web 服务器在响应头中添加正确的 MIME 类型非常重要。 如果配置不正确,浏览器可能会曲解文件内容…...
Linux之vim编辑器
vi编辑器是所有Unix及linux系统下标准的编辑器,类似于Windows系统下的记事本。很多软件默认使用vi作为他们编辑的接口。vim是进阶版的vi,vim可以视为一种程序编辑器。 前言: 1.文件准备 复制 /etc/passwd文件到自己的目录下(不…...
【工具介绍】可以批量查看LableMe标注的图像文件信息~
在图像处理和计算机视觉领域,LabelMe是一个广泛使用的图像标注工具,它帮助我们对图像中的物体进行精确的标注。但是,当标注完成后,我们常常需要一个工具来批量查看这些标注信息。 今天,我要介绍的这款exe程序…...
2024年山西省第十八届职业院校技能大赛 (高职组)“信息安全管理与评估”赛项规程
2024年山西省第十八届职业院校技能大赛 (高职组)“信息安全管理与评估”赛项规程 一、赛项名称 赛项名称:信息安全管理与评估 英文名称:Information Security Management and Evaluation 赛项组别:高职教师组 赛项归属…...
STM32完全学习——STemWin的移植小插曲
一、移植编译的一些问题 新版的STemWin的库没有区别编译器,只有一些这样的文件,默认你将这些文件导入到KEIL中,然后编译就会有下面的错误。 ..\MEWIN\STemWin\Lib\STemWin_CM4_wc16.a(1): error: A1167E: Invalid line start ..\MEWIN\STe…...
Java——IO流(下)
一 (字符流扩展) 1 字符输出流 (更方便的输出字符——>取代了缓冲字符输出流——>因为他自己的节点流) (PrintWriter——>节点流——>具有自动行刷新缓冲字符输出流——>可以按行写出字符串,并且可通过println();方法实现自动换行) 在Java的IO流中…...
avue-crud 同时使用 column 与 group 的问题
场景一:在使用option 中的column 和 group 进行表单数据新增操作时,进行里面的控件操作时,点击后卡死问题,文本没问题 其它比如下拉,单选框操作,当删除 column 中的字段后, group 中的可以操作 …...
深入解析 Pytest 中的 conftest.py:测试配置与复用的利器
在 Pytest 测试框架中,conftest.py 是一个特殊的文件,用于定义测试会话的共享配置和通用功能。它是 Pytest 的核心功能之一,可以用于以下目的: 【主要功能】 1、定义共享的 Fixture (1)conftest.py 文件可…...
JAVA |日常开发中Websocket详解
JAVA |日常开发中Websocket详解 前言一、Websocket 概述1.1 定义1.2 优势 二、Websocket 协议基础2.1 握手过程2.2 消息格式2.3 数据传输方式 三、Java 中使用 Websocket3.1 Java WebSocket API(JSR - 356)3.2 第三方库(如 Tyrus&…...
Typora教程
目录 一、下载安装 二、激活 1.激活 2.解决激活提示窗口 一、下载安装 去官网下载Typora安装,我的是1.9.5版本 二、激活 1.激活 根据路径找到Typora/resources/page-dist/static/js 使用记事本打开LicenseIndex文件,如下图: 按住快捷…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...
