手把手教你写一个图片预览组件

news2025/1/15 22:49:19

一、前言

本篇主要介绍,vue项目手写一个图片预览组件,组件主要包括图片方法、图片缩小、显示原图、下载、复制等功能。

二、实现方式

首先我们需要做一个图片预览组件都有的功能表头,如下图

主要功能包括,放大、缩放比例显示、缩小、原图比例、旋转、复制、下载,如果是多图预览的话,还有上一张、下一张。接下来我们逐个实现各个功能。

1.初始化窗口显示完整图片

我们在一打开图片预览的时候,就是要看到图片的全貌,有时候图片会比我们预览的窗口要大许多,因此我们需要初始化的时候计算图片的大小和窗口大小的对应比例。代码如下

<template><div class="preview-image"><ul class="header">..</ul><section ref="previewImage" class="preview-content dragscroll" :class="{'hideScroll' : hideScroll}" @wheel="handleMousewheel"><img ref="mediaElement":style="getStyles" :src="currentImgUrl" /></section></div>
</template>
<script> export default{data () {return {// 样式控制styles: {ratio: 0,rotate: 0,width: 0,height: 0,y: 0,x: 0,defaultWidth: 0,defaultHeight: 0},container: '',viewer: ''}},computed: {// 获取当前图片的urlcurrentImgUrl () {let result = this.urlif (this.list.length) {result = this.list[this.index]}return result},// 动态获取样式变动getStyles () {const styles = this.styleslet _styles = {}_styles = {transform: `rotate(${styles.rotate}deg)`,width: styles.width + 'px',height: styles.height + 'px',marginTop: styles.y + 'px',marginLeft: styles.x + 'px',zoom: styles.scale}return _styles},},methods: {// 初始化图片比例async initStyle (url) {// 初始设置旋转为0this.styles.rotate = 0const image = await this.loadImage(url)if (!image) return// 保存图片初始宽高this.styles.defaultWidth = image.widththis.styles.defaultHeight = image.height// 图片大小const width = image.widthconst height = image.height// 等待页面加载完成await this.$nextTick()// 获取窗口和图片this.container = this.$refs.previewImagethis.viewer = this.$refs.mediaElement// 可视区大小const containerWidth = this.container.clientWidthconst containerHeight = this.container.clientHeight// 可视区域宽度与图片实际宽度比例const initWidthScale = containerWidth / width// 可视区域高度与图片实际高度比例const initHeightScale = containerHeight / height// 优先缩放至比例最小的那个值if (initWidthScale < 1 || initHeightScale < 1) {let scale = initHeightScale > initWidthScale ? initWidthScale : initHeightScalethis.setZoomSize(scale)} else {this.setZoomSize(1)}this.$nextTick(() => {this.setMargin()})},// 加载一张图片,获取图片实例loadImage (url) {return new Promise((resolve, reject) => {const image = new Image()image.src = urlimage.onload = () => {resolve(image)}image.onerror = () => {const err = this.$t('im.loadErr')reject(err)}})},// 等比缩放setZoomSize (ratio) {this.styles.width = this.styles.defaultWidth * ratiothis.styles.height = this.styles.defaultHeight * ratiothis.styles.ratio = ratio},// 通过设置边距来计算图片位置setMargin () {if (this.container && this.viewer) {const contaienrSize = this.container.getBoundingClientRect()const imageSize = this.viewer.getBoundingClientRect()let y = (contaienrSize.height - imageSize.height) / 2let x = (contaienrSize.width - imageSize.width) / 2y = y < 0 ? 0 : yx = x < 0 ? 0 : xthis.styles.y = ythis.styles.x = x}},}
} </script> 

以上我们实现了图片的初始化显示

2.实现放大缩小逻辑

咱们这里主要css通过设置图片的缩放比例,利用zoom样式来实现图片的放大缩小。

 // 缩放,ratio为放大或缩小比例
handleZoom (ratio) {const styles = this.stylesconst abs = Math.abs(ratio)const changeScale = styles.ratio * absconst calScale = ratio > 0 ? changeScale : -changeScalelet _scale = styles.ratio + calScaleconst max = Math.min(this.SCALE_LIMIT.MAX, _scale) // 最大比例const min = Math.max(this.SCALE_LIMIT.MIN, _scale) // 最小比例console.log(_scale, max, this.SCALE_LIMIT.MAX)if (_scale > max) {_scale = max} else if (_scale < min) {_scale = min}_scale = parseFloat(_scale)this.setZoomSize(_scale)this.$nextTick(() => {this.setMargin()})
}, 

3.实现原始图片展示及复原

这里其实主要还是图片的缩放控制,代码如下

// 重置缩放比例, historyRatio记录原始大小之前的一次操作比例
handleResetZoom () {this.historyRatio = this.styles.ratiothis.setZoomSize(1)this.$nextTick(() => {this.setMargin()})
},
// 复原缩放比例, historyRatio
handleHistoryZoom () {this.setZoomSize(this.historyRatio)this.historyRatio = 0this.$nextTick(() => {this.setMargin()})
}, 

4.实现图片的翻转

这里主要是通过css的transform来实现的图片翻转,代码如下

// 旋转,配合样式的改变而改变
handleRotate () {this.styles.rotate += 90
}, 

5.实现图片的复制

这里主要用的clipboard剪切板和canvas来实现的图片复制,代码如下

// 复制图片
handleCopy() {var canvas = document.createElement('canvas') // 创建一个画板let image = new Image()image.setAttribute("crossOrigin", 'Anonymous') //可能会有跨越问题image.src = this.currentImgUrlimage.onload = () => {// img加载完成事件canvas.width = image.width//设置画板宽度canvas.height = image.height //设置画板高度canvas.getContext('2d').drawImage(image, 0, 0); //加载img到画板let url = canvas.toDataURL("image/png") // 转换图片为dataURL,格式为pngthis.clipboardImg(url) // 调用复制方法}
},
// 将图片写入到剪切板上
async clipboardImg (url) {try {const data = await fetch(url);const blob = await data.blob();await navigator.clipboard.write([new window.ClipboardItem({[blob.type]: blob})]);alert('复制成功')} catch (err) {alert('复制失败')}
}, 

6.实现图片的下载

这里就不说了,直接上代码吧

// 生成uuid
uuid (len, radix) {const chars = '0123456789abcdefghijklmnopqrstuvwxyz'.split('')const uuid = []let iradix = radix || 16len = len || 8radix = radix || chars.lengthif (len) {// Compact formfor (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]} else {// rfc4122, version 4 formlet r// rfc4122 requires these charactersuuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'uuid[14] = '4'// Fill in random data. At i==19 set the high bits of clock sequence as// per rfc4122, sec. 4.1.5for (i = 0; i < 36; i++) {if (!uuid[i]) {r = 0 | Math.random() * 16uuid[i] = chars[(i === 19) ? (r & 0x3) | 0x8 : r]}}}return uuid.join('')
},
// 下载
handleDownload () {fetch(this.currentImgUrl).then(res => res.blob().then(blob => {const a = document.createElement('a')const url = window.URL.createObjectURL(blob)const filename = this.uuid(8,16)a.href = url;a.download = filename;a.click();window.URL.revokeObjectURL(url);}));
}, 

7.实现ctrl + 滚轮事件缩放以及mac触摸板滚动放大缩小

这里主要通过监听鼠标的滚轮事件,来处理的缩放时间,触摸板同理

// 滚轮事件
handleMousewheel (evt) {this.wheelEvent(evt)
},
// ctrl + 滚轮事件缩放
wheelEvent (e) {if (Math.abs(e.deltaX) !== 0 && Math.abs(e.deltaY) !== 0) returnif (e.ctrlKey) {e.preventDefault()if (e.deltaY > 0) {this.handleZoom(-0.05)} else if (e.deltaY < 0) {this.handleZoom(0.05)}this.$nextTick(() => {this.setMargin()})}
}, 

以上就是逐个功能的实现代码一一说明。这些都是对于图片操作的主要实现方式。

三、后记

1. 全部代码及组件使用说明

图片预览组件的全部代码点击查看,下载代码下来复制直接使用,二次开发也可以。组件位置如图所示

其中index.vue为图片操作的各个封装,PreviewImageDialog.vue文件是做了个弹窗,用于弹窗显示图片预览内容,大家开发项目中肯定也有用到IU框架组件,到时候可以不用这个,直接引入index.vue组件, 放到dialog/modal等弹出窗中也是可以的。

2.关于dragToScroll

这里简单说下这个插件,它是用来将默认的拖拽变换成了scroll事件,在图片预览的时候,拖拽会变成滚动事件,用来查看全图。

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

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

相关文章

设计模式:02观察者模式--labview实现

引言 在观察者模式中&#xff0c;一种叫做被观察者的对象维护了观察者对象的集合&#xff0c;当被观察者对象发生改变时候&#xff0c;它会通知观察者。 在被观察者对象所维护的观察者集合中&#xff0c;能够添加或者删除观察者。被观察者状态变化能够传递给观察者。这样观察者…

路西德Lucid EDI项目测试流程

Lucid Motors路西德汽车拥有电动汽车制造、储能技术和代工生产等业务&#xff0c;目前已成功研制出其第一辆汽车Lucid Air&#xff0c;并开始对外销售。随着企业的不断发展&#xff0c;对自动化的要求也越来越高&#xff0c;作为制造型企业&#xff0c;Lucid早已实现机械自动化…

多个JDK版本可以吗:JDK17、JDK19、JDK1.8轻松切换(无坑版)小白也可以看懂

多个版本JDK切换 多个JDK&#xff1a;JDK17、JDK19、JDK1.8轻松切换&#xff08;无坑版&#xff09;小白也可以看懂 提示&#xff1a;看了网上很多教程&#xff0c;5w观看、32w观看、几千观看的&#xff0c;多多少少带点坑&#xff0c;这里我就把踩过的坑都给抹了 文章目录多个…

架构演进技巧

架构演进剖析 架构演进定义 定义&#xff1a;通过设计新的系统架构&#xff08;4R&#xff09;来应对业务和技术的发展变化 目的&#xff1a;1、应对业务发展带来新的复杂度&#xff1b;2、应用技术发展带来的复杂度新的解决方法 关键&#xff1a;1、新架构&#xff1b;2、…

UDS知识整理(五):安全访问——0x27服务

目录 一、0x27服务&#xff08;安全访问&#xff09;简介 二、0x27服务信息格式 &#xff08;1&#xff09;请求格式 &#xff08;2&#xff09;正响应格式 &#xff08;3&#xff09;负响应格式 三、0x27服务服务举例 &#xff08;1&#xff09;请求种子与发送KEY 一、…

实变函数与泛函分析基础

集合的运算 并集&#xff1a;1、任意两个集合 2、任意多个集合的并集或和集&#xff1a; 设 一族集合 &#xff1b;由一切 的 元素组成的集合&#xff0c;其中 是固定指标集&#xff0c; 是 中变化的指标。 记为 &#xff0c;可表示为 是 有限集&#xff0c; 记 …

CoreData 同步 iCloud 数据导致 App 启动超时被系统 watchdog 终止的原因及解决

问题现象 CoreData + iCloud 支持的 App 在启动时偶尔会出现被系统强制退出的情况,用 Xcode 也无法中断调试这种崩溃,查看真机上的崩溃日志如下: 如上图所示,我可以了解到 App 崩溃的原因是由于启动超时被系统看门狗(watchdog)强行关闭了: process-launch watchdog tra…

宇视雷达雷视交付|问题定位(素材收集篇)

雷达&雷视交付|问题定位&#xff08;素材收集篇&#xff09; 雷视一体机作为行业明星产品&#xff0c;具备交通信息采集、交通事件检测等功能&#xff0c;在交通领域的应用前景十分广阔。为了快速响应现场交付时问题定位的诉求&#xff0c;提升一线的素材收集规范性和效率…

回收站清空了怎么恢复?数据恢复,有这些就足够了

大家都知道&#xff0c;数据在回收站中会有一定的时间&#xff0c;但是被清空之后&#xff0c;数据就很难恢复了。回收站清空了怎么恢复&#xff1f;首先需要知道&#xff0c;被清空的文件可以分为两类。一类是重要资料&#xff0c;另一类是一些无关紧要的资料。找到我们要恢复…

Kotlin高仿微信-第14篇-单聊-视频通话

Kotlin高仿微信-项目实践58篇详细讲解了各个功能点&#xff0c;包括&#xff1a;注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。 Kotlin高仿…

SpringMVC(八):SSM整合

文章目录 SSM整合 一、准备数据库表格 二、创建maven web项目并补充项目结构&#xff0c;准备好MVC模式下的主要目录 三、更新web.xml 文件和准备包结构 四、导入依赖 五、log4j2.xml 六、jdbc.properties 七、springMVC.xml配置文件 八、applicationContext.xml 九、…

Anaconda默认安装在C:\Users\xxx\.conda\envs中

目录 问题&#xff1a; 解决&#xff1a; 更改默认安装位置 移动已安装环境 问题&#xff1a; 解决&#xff1a; 更改默认安装位置 用记事本打开 C:\Users\zqk\.condarc 在最后插入 envs_dirs: - D://anzhuang//Anaconda3//envs 如若需更改pkgs&#xff0c;插入如下代…

如何使用OpenCV作图像或矩阵的逻辑运算

所谓逻辑运算&#xff0c;主要是指逻辑与运算、逻辑或运算、逻辑非运算、逻辑异或运算。 可用函数bitwise_and()实现图像或矩阵的逻辑与运算&#xff1b; 可用函数bitwise_or()实现图像或矩阵的逻辑或运算&#xff1b; 可用函数bitwise_not()实现图像或矩阵的逻辑非运算&#x…

oh-my-zsh 为 ls 命令自定义颜色

ls 命令默认显示的颜色是&#xff1a; 白色&#xff1a; 表示普通文件 蓝色&#xff1a; 表示目录 绿色&#xff1a; 表示可执行文件 红色&#xff1a; 表示压缩文件 蓝绿色&#xff1a; 链接文件 红色闪烁&#xff1a;表示链接的文件有问题 黄色&#xff1a; 表示设备文件 灰…

Java实现3DES加密解密(DESede/ECB/PKCS5Padding使用)

一、简介 3DES&#xff08;又叫Triple DES&#xff09;是三重数据加密算法&#xff08;TDEA&#xff0c;Triple Data Encryption Algorithm&#xff09;块密码的通称。 它相当于是对每个数据块应用三次DES加密算法。密钥长度是128位&#xff0c;192位(bit)&#xff0c;如果密…

如何最简洁的使用iOS 开发证书 和 Profile 文件

如果你想在 iOS 设备&#xff08;iPhone/iPad/iTouch&#xff09;上调试&#xff0c; 需要有 iOS 开发证书和 Profile 文件。 在你拿到这两个文件之后&#xff0c;该如何使用呢&#xff1f; 证书使用说明&#xff1a; 1. iOS 开发证书&#xff1a;开发证书 &#xff08;Devel…

ROS——构建一个模拟的两轮ROS机器人

构建一个模拟的两轮ROS机器人1. 确保已经安装了Rviz2. 生成并构建ROS功能包2.1 生成一个catkin工作空间2.2 漏了一个懒得改2.3 构建差分驱动的机器人的URDF2.3.1 创建机器人底座2.3.2 使用roslaunch2.3.3 添加轮子2.3.4 添加颜色2.3.5 添加小脚轮2.3.6 添加碰撞属性2.3.7 移动轮…

CHS寻址

盘片&#xff08;platter&#xff09; 、磁头&#xff08;head&#xff09;、 磁道&#xff08;track&#xff09;、 扇区&#xff08;sector&#xff09;、 柱面&#xff08;cylinder&#xff09;。 CHS&#xff1a;通过柱面&#xff08;cylinder&#xff09;、磁头&#xff0…

成都易佰特的坑——E103-W06

写这篇博客&#xff0c;是为了记录&#xff0c;为了防止技术同行踩坑。 因为什么原因&#xff0c;就不说了。反正就是买了这个E103-W06模块来进行测试wifi透传的性能. 结果&#xff0c;好家伙&#xff0c;买回来就不能用。TMD虚拟串口都识别不了。还他妈卖的挺贵。95元~99元一…

【附源码】计算机毕业设计JAVA装修网站

【附源码】计算机毕业设计JAVA装修网站 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA mybatis M…