H5移动端图片查看器

news/2025/10/13 11:52:41/文章来源:https://www.cnblogs.com/bokemoqi/p/19138105

一、新建名为ImageViewer.vue的组件,代码如下:

<!-- ImageViewer.vue -->
<template><div class="fullscreen-viewer" v-if="visible" @click="closeViewer"><div class="viewer-container" @click.stop><div class="image-wrapper" @wheel="handleWheel" @mousedown="startDrag" @mousemove="dragging" @mouseup="endDrag"@mouseleave="endDrag" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd"@touchcancel="handleTouchEnd" :style="transformStyle"><img :src="imageUrl" :alt="altText" draggable="false"></div><div class="controls"><span class="zoom-info">{{ Math.round(scale * 100) }}%</span><button class="close-btn" @click="closeViewer">×</button></div></div></div>
</template><script setup>
import { ref, computed } from 'vue'const props = defineProps({imageUrl: {type: String,required: true},altText: {type: String,default: '预览图片'}
})const visible = ref(false)
const scale = ref(1)
const position = ref({ x: 0, y: 0 })
const isDragging = ref(false)
const dragStart = ref({ x: 0, y: 0 })
const touchStart = ref({ x: 0, y: 0, distance: 0, scale: 1 })const transformStyle = computed(() => ({transform: `translate(${position.value.x}px, ${position.value.y}px) scale(${scale.value})`,cursor: isDragging.value ? 'grabbing' : 'grab'
}))const openViewer = () => {visible.value = truescale.value = 1position.value = { x: 0, y: 0 }
}const closeViewer = () => {visible.value = falseisDragging.value = falseposition.value = { x: 0, y: 0 }
}const handleWheel = (e) => {e.preventDefault()const delta = e.deltaY > 0 ? -0.1 : 0.1let newScale = scale.value + deltanewScale = Math.max(0.9, Math.min(3, newScale))scale.value = newScale
}const startDrag = (e) => {isDragging.value = truedragStart.value = {x: e.clientX - position.value.x,y: e.clientY - position.value.y}
}const dragging = (e) => {if (!isDragging.value) returnconst newX = e.clientX - dragStart.value.xconst newY = e.clientY - dragStart.value.y// 计算边界限制const maxX = Math.max(0, (scale.value - 1) * window.innerWidth / 2)const maxY = Math.max(0, (scale.value - 1) * window.innerHeight / 2)position.value = {x: Math.max(-maxX, Math.min(maxX, newX)),y: Math.max(-maxY, Math.min(maxY, newY))}
}const endDrag = () => {isDragging.value = false
}const handleTouchStart = (e) => {if (e.touches.length === 1) {isDragging.value = truedragStart.value = {x: e.touches[0].clientX - position.value.x,y: e.touches[0].clientY - position.value.y}} else if (e.touches.length === 2) {isDragging.value = falseconst touch1 = e.touches[0]const touch2 = e.touches[1]const distance = Math.sqrt(Math.pow(touch2.clientX - touch1.clientX, 2) +Math.pow(touch2.clientY - touch1.clientY, 2))touchStart.value = {x: (touch1.clientX + touch2.clientX) / 2,y: (touch1.clientY + touch2.clientY) / 2,distance: distance,scale: scale.value}}
}const handleTouchMove = (e) => {e.preventDefault()if (e.touches.length === 1 && isDragging.value) {if (e.touches.length === 1 && isDragging.value) {const newX = e.touches[0].clientX - dragStart.value.xconst newY = e.touches[0].clientY - dragStart.value.y// 计算边界限制const maxX = Math.max(0, (scale.value - 1) * window.innerWidth / 2)const maxY = Math.max(0, (scale.value - 1) * window.innerHeight / 2)position.value = {x: Math.max(-maxX, Math.min(maxX, newX)),y: Math.max(-maxY, Math.min(maxY, newY))}}} else if (e.touches.length === 2) {const touch1 = e.touches[0]const touch2 = e.touches[1]const distance = Math.sqrt(Math.pow(touch2.clientX - touch1.clientX, 2) +Math.pow(touch2.clientY - touch1.clientY, 2))const scaleChange = distance / touchStart.value.distancescale.value = Math.max(0.9, Math.min(3, touchStart.value.scale * scaleChange))const centerX = (touch1.clientX + touch2.clientX) / 2const centerY = (touch1.clientY + touch2.clientY) / 2position.value = {x: position.value.x + (centerX - touchStart.value.x),y: position.value.y + (centerY - touchStart.value.y)}touchStart.value.x = centerXtouchStart.value.y = centerY}
}const handleTouchEnd = () => {isDragging.value = false
}defineExpose({openViewer,closeViewer
})
</script><style scoped>
.fullscreen-viewer {position: fixed;top: 0;left: 0;width: 100%;height: 100%;background-color: rgba(0, 0, 0, 0.9);display: flex;justify-content: center;align-items: center;z-index: 9999;
}.viewer-container {position: relative;width: 100%;height: 100%;display: flex;flex-direction: column;
}.image-wrapper {flex: 1;display: flex;justify-content: center;align-items: center;overflow: hidden;transition: transform 0.1s ease-out;touch-action: none;
}.image-wrapper img {max-width: 100%;max-height: 100%;object-fit: contain;user-select: none;-webkit-user-drag: none;
}.controls {position: absolute;bottom: 30px;right: 20px;display: flex;gap: 10px;align-items: center;
}.zoom-info {color: white;background-color: rgba(0, 0, 0, 0.5);padding: 5px 10px;border-radius: 4px;font-size: 14px;
}.close-btn {width: 36px;height: 36px;border-radius: 50%;background-color: rgba(0, 0, 0, 0.5);color: white;border: none;cursor: pointer;font-size: 22px;display: flex;justify-content: center;align-items: center;transition: background-color 0.3s;
}.close-btn:hover {background-color: rgba(255, 255, 255, 0.3);
}@media (max-width: 768px) {.controls {bottom: 30px;right: 20px;}.zoom-info {font-size: 12px;padding: 3px 8px;}.close-btn {width: 36px;height: 36px;font-size: 22px;}
}
</style>

二、引入组件使用

<!-- index.vue -->
<template><div><!-- 点击图片打开查看器 --><img src="your-image-url" @click="openImageViewer" /><!-- 图片查看器组件 --><ImageViewer ref="imageViewer"imageUrl="your-image-url"altText="图片描述"/></div>
</template><script setup>
import { ref } from 'vue'
import ImageViewer from './ImageViewer.vue'const imageViewer = ref(null)const openImageViewer = () => {imageViewer.value.openViewer()
}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/936017.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年10月复合钢丝网厂家最新推荐排行榜,镀锌复合钢丝网,不锈钢复合钢丝网,建筑用复合钢丝网公司推荐!

2025年10月复合钢丝网厂家最新推荐排行榜:镀锌、不锈钢及建筑用复合钢丝网公司推荐随着工业和建筑业的快速发展,复合钢丝网在多个领域的应用越来越广泛。无论是用于建筑加固、防护围栏还是其他用途,选择一家优质的复…

typora无需激活版及最新激活版方法!双击安装就能用

软件介绍 Typora 是一款Markdown编辑器,支持实时预览,所见即所得。跨平台,支持Windows、macOS、Linux。适合写作、笔记、技术文档等。本教程将提供‌合法安全‌的安装方案,并解决常见问题,助你高效完成部署!直接…

类和对象(二) - 实践

类和对象(二) - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &quo…

2025 年国内风化板源头厂家最新推荐排行榜:聚焦优质原料与精湛工艺,助力消费者精准选购靠谱企业榜单吧台/松木/桌面/茶台风化板厂家推荐

引言当前风化板市场需求持续增长,但行业乱象也让消费者选购时倍感困扰。一方面,大量厂家涌入导致产品质量参差不齐,部分企业为压缩成本使用劣质木材,使风化板易出现变形、开裂问题,严重影响使用体验;另一方面,市…

20232312 2025-2026-1 《网络与系统攻防技术》实验一实验报告

20232312 2025-2026-1 《网络与系统攻防技术》实验一实验报告20232312 2025-2026-1 《网络与系统攻防技术》实验一实验报告 1.实验内容 1.1通过学习、分析可执行文件pwn1,学习了三种漏洞利用技术:​​直接修改程序机…

2025年10月方钢厂家最新推荐排行榜,热轧方钢,冷拉方钢,高强度方钢,优质方钢供应商推荐!

2025年10月方钢厂家最新推荐排行榜:热轧方钢、冷拉方钢、高强度方钢、优质方钢供应商推荐随着工业和建筑业的快速发展,方钢作为重要的建筑材料之一,其需求量持续增长。为了帮助筛选方钢品牌,特此发布权威推荐榜单,…

OpenBLAS blas_thread_init: pthread_create failed for thread 1 of 4: Operation not permitted

https://blog.csdn.net/qq_45237725/article/details/148383599 (加权限)无可奈何花落去,似曾相识燕归来

QPSK调制在瑞利、高斯和莱斯信道下的MATLAB仿真

QPSK调制在不同信道条件下性能仿真的MATLAB实现 %% QPSK在瑞利、高斯和莱斯信道下的仿真 clear; close all; clc;%% 仿真参数设置 numBits = 1e6; % 传输的比特数 SNR_dB = 0:2:20; % 信噪比范…

Delapp文件删除工具!Windows中删除文件和文件夹的简单工具!仅507KB的工具小巧且方便

有的时候我们删文件总是遇到无法删除,提示文件在另一程序打开,但是又没有打开,怎么删也删不掉, ​​ 软件介绍 Delapp 是一款开源免费的Windows文件删除工具,免安装、小巧、速度快、支持win7……帮你解除占用,爽…

在 2023 年屌爆了一整年的 shadcn/ui 用的 Headless UI 到底是何方神圣?

在 2023 年屌爆了一整年的 shadcn/ui 用的 Headless UI 到底是何方神圣? 2024-03-1113,974阅读8分钟 专栏: Headless UI 无头组件的介绍与实现 作者:易师傅 、github 声明:本文为稀土掘金技术社区首发签约文章…

基于Hadoop+Spark的商店购物趋势分析与可视化平台科技达成

基于Hadoop+Spark的商店购物趋势分析与可视化平台科技达成pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consola…

2025 年折弯厂家推荐:江阴市富磊钢板加工专业中厚钢板折弯加工与高效行业解决方案提供商

行业背景随着船舶设备、港口重工、换热设备、环保设备等重工行业的快速发展,市场对中厚钢板折弯加工的需求日益提升,尤其是超厚、超长、大吨位的钢板折弯加工需求,对加工企业的技术实力、设备配置和产能规模提出了更…

2025年10月振动电机厂家最新推荐排行榜,三相振动电机,单相振动电机,防爆振动电机公司推荐!

2025年10月振动电机厂家最新推荐排行榜:三相、单相及防爆振动电机公司推荐随着工业自动化和智能化的不断发展,振动电机在各个行业的应用越来越广泛。从矿山、冶金到食品加工、化工等领域,振动电机都发挥着重要作用。…

2025 储能 EMS 厂商排名:五大品牌以全维度优势领跑,技术与规模双驱动企业凸显

在新型电力系统建设加速推进的背景下,储能 EMS 作为储能电站的 “大脑”,其厂商的综合实力直接决定系统运行效率与价值实现。2025 年市场竞争已从场景适配转向全维度实力比拼,资质认证的完备性、核心技术的突破性、…

【IEEE出版、连续6届已EI检索、多校联办】第七届机器人、智能控制与人工智能国际学术会议(RICAI 2025)

第七届机器人、智能控制与人工智能国际学术会议(RICAI 2025) 2025 7th International Conference on Robotics, Intelligent Control and Artificial Intelligence IEEE出版(ISBN: 979-8-3315-6934-1),IEEE Xplor…

企业数字化转型浪潮下,如何选择最适合的项目管理工具?

企业数字化转型浪潮下,如何选择最适合的项目管理工具? 随着数字化转型进程加速,项目管理工具已成为企业提升协作效率的关键基础设施。根据Gartner最新调研数据,2023年全球项目管理软件市场规模达到59亿美元,年增长…

dify工作流遇到的问题及解决方案

dify工作流遇到的问题及解决方案1、在通过dify粘贴文件url上传时遇到的报错:报错显示无效的url,在浏览器输入这串url能获取到文件,说明是dify读取文件的问题 通过浏览器开发者模式可以得出结果:缺少本地url头 在di…

2025年10月青海视频号运营最新权威推荐榜:专业服务与创意内容引领潮流!

2025年10月青海视频号运营最新权威推荐榜:专业服务与创意内容引领潮流!随着移动互联网的快速发展,短视频平台已经成为企业宣传和品牌推广的重要渠道。在青海地区,视频号作为一种新兴的营销工具,正逐渐受到越来越多…

2025 年玻璃钢水箱生产厂家最新推荐榜单:含 30 吨 / 订做 / 消防 / 方形 / 拼装式 / 屋顶 / 大型产品,从产能与服务维度精选优质企业

当前建筑、化工、食品、医药等行业对玻璃钢水箱的需求日益增长,但市场乱象却让采购者陷入困境。多数厂家存在技术储备薄弱问题,产品质量波动大,难以满足行业严苛标准;部分厂家服务体系残缺,售前咨询模糊、售后响应…

[C++工程框架]gflags和gtest的简单介绍

[C++工程框架]gflags和gtest的简单介绍2025-10-13 11:21 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !…