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文件,如下图: 按住快捷…...
AWS WebRTC:获取ICE服务地址(part 1)
建立WebRTC连接的第二步是获取ICE服务地址。 ICE全称:Interactive Connectivity Establishment,建立互动连接。 ICE 服务地址,主要是 TURN 和 STUN 服务器的地址,用于 WebRTC 在 NAT 网络环境中协商建立连接。 上代码ÿ…...
@Docker Compose部署Alertmanager
文章目录 Docker Compose部署Alertmanager1. 准备工作1.1 系统要求1.2 目录结构准备 2. 配置文件准备2.1 创建docker-compose.yml文件2.2 创建Alertmanager配置文件 3. 部署Alertmanager3.1 启动服务3.2 验证服务状态3.3 检查日志 4. 服务验证4.1 访问Web UI 4.2 API健康检查5.…...

【图像处理入门】2. Python中OpenCV与Matplotlib的图像操作指南
一、环境准备 import cv2 import numpy as np import matplotlib.pyplot as plt# 配置中文字体显示(可选) plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False二、图像的基本操作 1. 图像读取、显示与保存 使用OpenCV…...

通过mqtt 点灯
1 解析mqtt 传过来的json 用cjson 解析。 2 类似mvc的结构,调用具体的动作函数 定义设备处理结构体:使用结构体数组映射设备名称与处理函数,实现可扩展的指令分发分离设备逻辑:为每个设备(如 LED、Motor࿰…...

如何手搓扫雷(待扩展)
文章目录 一、扫雷游戏分析与设计1.1 扫雷游戏的功能说明1.2 游戏的分析和设计1.2.1 数据结构的分析1.2.2 文件结构设计 二、扫雷游戏的代码实现三、扫雷游戏的扩展总结 一、扫雷游戏分析与设计 扫雷游戏网页版 1.1 扫雷游戏的功能说明 使用控制台(黑框框的程序&a…...
微信小程序(uniapp)对接腾讯云IM
UniApp 对接腾讯云 IM(即时通讯)完整指南 一、项目背景与需求分析 随着社交场景的普及,即时通讯功能已成为移动应用的标配。腾讯云 IM(Tencent IM,即 TIM)提供稳定可靠的即时通讯服务,支持单聊…...

多台电脑共用一个ip地址可以吗?会怎么样
在互联网使用日益普及的今天,许多人都面临着多台设备共享网络的需求。一个常见的问题随之而来:多台电脑共用一个IP地址可以吗?这样做会带来哪些影响?本文将深入探讨这一话题。 一、多台电脑共用一个IP地址可以吗? 多…...

VScode ios 模拟器安装cocoapods
使用 Homebrew 安装(推荐) 如果你有 Homebrew,直接用它安装更稳定: brew install cocoapods...

VR/AR 视网膜级显示破局:10000PPI 如何终结颗粒感时代?
一、传统液晶 “纱窗效应”:VR 沉浸体验的最大绊脚石 当用户首次戴上 VR 头显时,眼前密密麻麻的像素网格往往打破沉浸感 —— 这正是传统液晶显示在近眼场景下的致命缺陷。受限于 500-600PPI 的像素密度,即使达到 4K 分辨率,等效到…...
(19)java在区块链中的应用
🔗 Java在区块链中的应用:智能合约开发全攻略 TL;DR: Java在区块链领域主要通过Hyperledger Fabric、Web3j和专用JVM实现智能合约开发,相比Solidity具有更强的企业级支持和开发效率,但在执行效率和Gas消耗方面存在差异,…...