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文件,如下图: 按住快捷…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...