当前位置: 首页 > news >正文

viewerjs 如何新增下载图片功能(npm包补丁)

文章目录

    • 先实现正常的效果
    • 实现下载图片
    • 改变viewerjs的build函数
    • 源码改变之后,执行npm i 之后node_modules源码又变回了原样

1、viwerjs所有功能都很完善,但唯独缺少了图片的下载
2、需求:在用viwerjs旋转图片后,可以直接下载旋转后的图片

效果:
在这里插入图片描述

先实现正常的效果

1、安装v-viewer(一个对viwerjs的使用方式优化的npm包)

npm i v-viewer

2、

main.js文件 (vue2版本)

import Vue from 'vue'
import App from './App.vue'
//哪怕使用v-viewer也需要导入正常的viwerjs的css文件
import 'viewerjs/dist/viewer.css'
import Viewer from 'v-viewer'Vue.use(Viewer, {defaultOptions: {//里面可以填一些配置项,这里我先不填}
})new Vue({render: h => h(App),
}).$mount('#app')

app.vue文件中使用组件

<template><div class="main"><viewer ref="viewer"><imgsrc="https://img1.baidu.com/it/u=413643897,2296924942&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500"alt=""data-uid="uid字段"ref="img"/></viewer></div>
</template>
<script>export default {data() {return {};},props: {},watch: {},mounted() {},created() {},methods: {},};
</script>
<style scoped>
</style>

效果
在这里插入图片描述

实现下载图片

在main.js中添加配置项
可以先看下面build函数的改动,再看main中的download

import Vue from 'vue'
import App from './App.vue'
import 'viewerjs/dist/viewer.css'
import Viewer from 'v-viewer'
Vue.config.productionTip = false
//可以先忽略base64toBlob函数,就是个base64文件转blob的函数
function base64toBlob(dataurl) {var arr = dataurl.split(','),mime = arr[0].match(/:(.*?);/)[1],bstr = atob(arr[1]),n = bstr.length,u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}return new Blob([u8arr], {type: mime});
}
Vue.use(Viewer, {defaultOptions: {//下载事件的回调download(viewer) {//当前显示图片的索引const index = viewer.index//所有图片构成的数组const list = viewer.images//当前显示的图片const ima = list[index]const canvas = document.createElement('canvas') //获取canvas//对应的CanvasRenderingContext2D对象(画笔)let img = new Image() //创建新的图片对象let base64 = ''; //base64 img.src = ima.src + '?' + new Date().getTime();//如果您仅仅是下载当前图片, img.src便是当前的图片地址   //下面的内容就完全可以不用看了//自行百度:前端拿到图片地址,如何下载图片const rotate = viewer.imageData.rotatelet x, yconst type = rotate / 90 % 4img.setAttribute("crossOrigin", 'Anonymous')img.onload = function () { //图片加载完,再draw 和 toDataURLlet width = img.widthlet height = img.heightconst flg = width == height//这里写的不是很好(4个if判断),有更好的书写方式可以告知我一下,除了(switch、改成object的形式)//0、1、2、3对应旋转0°、90°、180°、270°//0的时候不需要特处理if (type == 0) {x = 0;y = 0} else if (type == 1) {//1的时候需要考虑一种清空,如果他是个矩形图片,也就是宽高不相等//在旋转90°后,我们需要调整canvas的宽高、同理270°一样x = height;y = 0;if (!flg) {const blg = widthwidth = heightheight = blg}} else if (type == 2) {x = width;y = height;} else if (type == 3) {x = 0;y = height;if (!flg) {const blg = widthwidth = heightheight = blg}}canvas.width = widthcanvas.height = heightlet ctx = canvas.getContext("2d")ctx.translate(x, y);ctx.rotate(rotate * Math.PI / 180);ctx.drawImage(img, 0, 0, img.width, img.height);//图片转base64base64 = canvas.toDataURL("image/png");//base64转链接,该链接便是用户旋转后的图片了//如果想下载该图片,直接定义a标签,然后触发点击事件下载即可,可自行百度拿到链接,如何下载链接的内容console.log('url___', URL.createObjectURL(base64toBlob(base64)));}}}
})new Vue({render: h => h(App),router,
}).$mount('#app')

找到viwerjs源文件
/node_modules/viewerjs/dist/viewer.js
为什么不去v-viewer文件下找呢?上面说了,v-viewer是对viwwerjs的封装使用,似乎没有改动源码
在这里插入图片描述
在该文件下搜索

toolbar.appendChild(list)

该代码存在于build函数下面,作用是构建底部的工具按钮

改变viewerjs的build函数

build函数源代码(看我写的注释)
其中注释最多的地方便是对源码的改动

{key: "build",value: function build() {if (this.ready) {return;}//关键变量之一,当前viewerjs挂载的domvar element = this.element,//关键变量,当前的viewer的option配置项options = this.options;var parent = element.parentNode;var template = document.createElement('div');template.innerHTML = TEMPLATE;var viewer = template.querySelector(".".concat(NAMESPACE, "-container"));var title = viewer.querySelector(".".concat(NAMESPACE, "-title"));//toolbar 关键变量之一,下面代码是获取toolbar的dom元素var toolbar = viewer.querySelector(".".concat(NAMESPACE, "-toolbar"));var navbar = viewer.querySelector(".".concat(NAMESPACE, "-navbar"));var button = viewer.querySelector(".".concat(NAMESPACE, "-button"));var canvas = viewer.querySelector(".".concat(NAMESPACE, "-canvas"));this.parent = parent;this.viewer = viewer;this.title = title;this.toolbar = toolbar;this.navbar = navbar;this.button = button;this.canvas = canvas;this.footer = viewer.querySelector(".".concat(NAMESPACE, "-footer"));this.tooltipBox = viewer.querySelector(".".concat(NAMESPACE, "-tooltip"));this.player = viewer.querySelector(".".concat(NAMESPACE, "-player"));this.list = viewer.querySelector(".".concat(NAMESPACE, "-list"));viewer.id = "".concat(NAMESPACE).concat(this.id);title.id = "".concat(NAMESPACE, "Title").concat(this.id);addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title) ? options.title[0] : options.title));addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));toggleClass(button, CLASS_HIDE, !options.button);if (options.keyboard) {button.setAttribute('tabindex', 0);}if (options.backdrop) {addClass(viewer, "".concat(NAMESPACE, "-backdrop"));if (!options.inline && options.backdrop !== 'static') {setData(canvas, DATA_ACTION, 'hide');}}if (isString(options.className) && options.className) {// In case there are multiple class namesoptions.className.split(REGEXP_SPACES).forEach(function (className) {addClass(viewer, className);});}if (options.toolbar) {var list = document.createElement('ul');var custom = isPlainObject(options.toolbar);var zoomButtons = BUTTONS.slice(0, 3);var rotateButtons = BUTTONS.slice(7, 9);var scaleButtons = BUTTONS.slice(9);if (!custom) {addClass(toolbar, getResponsiveClass(options.toolbar));}//关键函数之一,forEach,是对传统foreach的1封装//BUTTONS关键变量之一,是一个数组,里面存放的是底部操作工具栏信息//下面的foreach的解读:根据BUTTONS这个数组构建出底部工具栏按钮,并且给对应的按钮添加一些属性信息forEach(custom ? options.toolbar : BUTTONS, function (value, index) {var deep = custom && isPlainObject(value);var name = custom ? hyphenate(index) : value;var show = deep && !isUndefined(value.show) ? value.show : value;if (!show || !options.zoomable && zoomButtons.indexOf(name) !== -1 || !options.rotatable && rotateButtons.indexOf(name) !== -1 || !options.scalable && scaleButtons.indexOf(name) !== -1) {return;}var size = deep && !isUndefined(value.size) ? value.size : value;var click = deep && !isUndefined(value.click) ? value.click : value;var item = document.createElement('li');if (options.keyboard) {item.setAttribute('tabindex', 0);}item.setAttribute('role', 'button');addClass(item, "".concat(NAMESPACE, "-").concat(name));if (!isFunction(click)) {setData(item, DATA_ACTION, name);}if (isNumber(show)) {addClass(item, getResponsiveClass(show));}if (['small', 'large'].indexOf(size) !== -1) {addClass(item, "".concat(NAMESPACE, "-").concat(size));} else if (name === 'play') {addClass(item, "".concat(NAMESPACE, "-large"));}if (isFunction(click)) {addListener(item, EVENT_CLICK, click);}list.appendChild(item);});toolbar.appendChild(list);} else {addClass(toolbar, CLASS_HIDE);}//代码走到这一步,表示toolbar基本按钮已经配置完成了//在这里来添加我们自己想加的按钮//下面代码是我添加的功能//开始//isFunction:判断传递的download是不是对象,isFunction是viwerjs作者封装好的方法//addListener:给某个dom元素添加事件监听,同样是viwerjs作者封装好的//addListener(参数一是dom元素,参数二是事件名称,参数三是事件回调)//addListener会有参数4,参数4是元素监听器的一些配置项,非必填if(isFunction(options.download)){//创建保存按钮var download = document.createElement("li")var that = this//给按钮加事件addListener(download,EVENT_CLICK,function(){// options.download就是我上面main.js传递的download函数//在点击该按钮后,同时执行download回调//传递参数,that.element是viwerjs挂载的dom,//that.element.viewer,viwerjs挂载后会为挂载的元素添加viewer属性(巨全的属性,里面啥都有),可以说我们拿到viewer基本所有的内容都可以拿到了options.download(that.element.viewer)})//addclass是别人封装好的,给元素加类名,加完类名后直接改变css样式就行了,或者直接用行内styleaddClass(download,'viewerjs-download')list.appendChild(download)}///结束if (!options.rotatable) {var rotates = toolbar.querySelectorAll('li[class*="rotate"]');addClass(rotates, CLASS_INVISIBLE);forEach(rotates, function (rotate) {toolbar.appendChild(rotate);});}if (options.inline) {addClass(button, CLASS_FULLSCREEN);setStyle(viewer, {zIndex: options.zIndexInline});if (window.getComputedStyle(parent).position === 'static') {setStyle(parent, {position: 'relative'});}parent.insertBefore(viewer, element.nextSibling);} else {addClass(button, CLASS_CLOSE);addClass(viewer, CLASS_FIXED);addClass(viewer, CLASS_FADE);addClass(viewer, CLASS_HIDE);setStyle(viewer, {zIndex: options.zIndex});var container = options.container;if (isString(container)) {container = element.ownerDocument.querySelector(container);}if (!container) {container = this.body;}container.appendChild(viewer);}if (options.inline) {this.render();this.bind();this.isShown = true;}this.ready = true;if (isFunction(options.ready)) {addListener(element, EVENT_READY, options.ready, {once: true});}if (dispatchEvent(element, EVENT_READY) === false) {this.ready = false;return;}if (this.ready && options.inline) {this.view(this.index);}}/*** Get the no conflict viewer class.* @returns {Viewer} The viewer class.*/}

源码改变之后,执行npm i 之后node_modules源码又变回了原样

利用patch-package解决该问题

安装

npm i patch-package

安装完成后,执行命令

 npx patch-package viewerjs//viewerjs是我们修改node_modules中的源码文件名 npx patch-package为固定内容

执行命令后发现,项目多了一个patches文件,这个便是对源码的补丁,我们需要把这个文件上传至项目仓库中,然后你同事拉下代码,执行npm i后会自动使用我们写的补丁
在这里插入图片描述

相关文章:

viewerjs 如何新增下载图片功能(npm包补丁)

文章目录 先实现正常的效果实现下载图片改变viewerjs的build函数源码改变之后&#xff0c;执行npm i 之后node_modules源码又变回了原样 1、viwerjs所有功能都很完善&#xff0c;但唯独缺少了图片的下载 2、需求&#xff1a;在用viwerjs旋转图片后&#xff0c;可以直接下载旋转…...

基于YOLOv7开发构建MSTAR雷达影像目标检测系统

MSTAR&#xff08;Moving and Stationary Target Acquisition and Recognition&#xff09;数据集是一个基于合成孔径雷达&#xff08;Synthetic Aperture Radar&#xff0c;SAR&#xff09;图像的目标检测和识别数据集。它是针对目标检测、机器学习和模式识别算法的研究和评估…...

关于c++中mutable、const、volatile这三个关键字及对应c++与汇编示例源码

这哥三之间的关系是有趣的&#xff0c;不妨看看这个&#xff1a; cv (const and volatile) type qualifiers - cppreference.com mutable permits modification of the class member declared mutable even if the containing object is declared const. 即便一个对象是con…...

把大模型装进手机,分几步?

点击关注 文 | 姚 悦 编 | 王一粟 大模型“跑”进手机&#xff0c;AI的战火已经从“云端”烧至“移动终端”。 “进入AI时代&#xff0c;华为盘古大模型将会来助力鸿蒙生态。”8月4日&#xff0c;华为常务董事、终端BG CEO、智能汽车解决方案BU CEO 余承东介绍&#xff0c…...

c++游戏制作指南(三):c++剧情类文字游戏的制作

&#x1f37f;*★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* &#x1f37f; &#x1f35f;欢迎来到静渊隐者的csdn博文&#xff0c;本文是c游戏制作指南的一部&#x1f35f; &#x1f355;更多文章请点击下方链接&#x1f355; &#x1f368; c游戏制作指南&#x1f3…...

Flutter系列文章-实战项目

在本篇文章中&#xff0c;我们将通过一个实际的 Flutter 应用来综合运用最近学到的知识&#xff0c;包括保存到数据库、进行 HTTP 请求等。我们将开发一个简单的天气应用&#xff0c;可以根据用户输入的城市名获取该城市的天气信息&#xff0c;并将用户查询的城市列表保存到本地…...

HCIA---TCP/UDP协议

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 目录 文章目录 一.UDP协议简介 UDP协议的特点&#xff1a; 二.TCP协议简介 TCP协议特点 三.TCP和UDP的区别 思维导图 一.UDP协议简介 UDP&#xff08;User …...

数据库索引的使用

1、MySQL的基本架构 架构图 左边的client可以看成是客户端&#xff0c;客户端有很多&#xff0c;像我们经常你使用的CMD黑窗口&#xff0c;像我们经常用于学习的WorkBench&#xff0c;像企业经常使用的Navicat工具&#xff0c;它们都是一个客户端。右边的这一大堆都可以看成是…...

校验 GPT-4 真实性的三个经典问题:快速区分 GPT-3.5 与 GPT-4,并提供免费测试网站

现在已经有很多 ChatGPT 的套壳网站&#xff0c;以下分享验明 GPT-4 真身的三个经典问题&#xff0c;帮助你快速区分套壳网站背后到底用的是 GPT-3.5 还是 GPT-4。 大家可以在这个网站测试&#xff1a;https://ai.hxkj.vip&#xff0c;免登录可以问三条&#xff0c;登录之后无限…...

SpringBoot整合MongoDB连接池(含源码)

&#x1f4a1;版本依赖 jdk 17 SpringBoot 3.1.0 Mongo 6.0.8 mybatis-plus 2.0.2 &#x1f4a1;环境准备 &#x1f335;MongoDB安装 安装教程请查看&#xff1a;一文搞定(linuxwindowsdocker)安装MongoDB &#x1f335;导入依赖 <parent><groupId>org.sp…...

[oeasy]python0082_[趣味拓展]控制序列_清屏_控制输出位置_2J

光标位置 回忆上次内容 上次了解了键盘演化的过程 ESC 从 组合键到 独立按键 ESC的作用 是 进入 控制序列配置 控制信息控制信息 \033[y;xH 设置光标位置\033[2J 清屏 这到底怎么控制&#xff1f;&#xff1f;&#xff1f;&#x1f914;谁来实现这些功能&#xff1f; 控制…...

Zookeeper+kafka

目录 1. Zookeeper定义 2. Zookeeper工作机制 3. Zookeeper特点 4. Zookeeper数据结构 5. Zookeeper应用场景 5.1 统一命名服务 5.2 统一配置管理 5.3 统一集群管理 5.4 服务器动态上下线 5.5 软负载均衡 6. Zookeeper 选举机制 6.1 第一次启动选举机制 6.2 非第一…...

Gpt微信小程序搭建的前后端流程 - 前端小程序部分-1.基础页面框架的静态设计(二)

Gpt微信小程序搭建的前后端流程 - 前端小程序部分-1.基础页面框架的静态设计(二) 在开始这个专栏&#xff0c;我们需要找一个小程序为参考&#xff0c;参考和仿照其界面&#xff0c;聊天交互模式。 这里参考小程序-小柠AI智能聊天&#xff0c;可自行先体验。 该小程序主要提供了…...

Flask进阶:构建RESTful API和数据库交互

在初级教程中&#xff0c;我们已经介绍了如何使用Flask构建基础的Web应用。在本篇中级教程中&#xff0c;我们将学习如何用Flask构建RESTful API&#xff0c;以及如何使用Flask-SQLAlchemy进行数据库操作。 一、构建RESTful API REST&#xff08;Representational State Tran…...

6.9(Java)二叉搜索树

1.我的代码: public class BinarySearchTree {class TreeNode {public int key;public TreeNode left;public TreeNode right;public TreeNode(int key) {this.key key;}}public TreeNode root; // 根节点// 插入一个元素,注意&#xff0c;不能插入重复的值&#xff0c;如…...

洛谷P2256 一中校运会之百米跑

题目背景 在一大堆秀恩爱的 ** 之中&#xff0c;来不及秀恩爱的苏大学神踏着坚定&#xff08;&#xff1f;&#xff09;的步伐走向了 100 100 100 米跑的起点。这时苏大学神发现&#xff0c;百米赛跑的参赛同学实在是太多了&#xff0c;连体育老师也忙不过来。这时体育老师发…...

python-opencv对极几何 StereoRectify

OpenCV如何正确使用stereoRectify函数 函数介绍 用于双目相机的立体校正环节中&#xff0c;这里只谈谈这个函数怎么使用&#xff0c;参数具体指哪些函数参数 随便去网上一搜或者看官方手册就能得到参数信息&#xff0c;但是&#xff01;&#xff01;相对关系非常容易出错&…...

pom文件---maven

027-Maven 命令行-实验四-生成 Web 工程-执行生成_ev_哔哩哔哩_bilibili 27节.后续补充 一.maven下载安装及配置 1)maven下载 2) settings文件配置本地仓库 3)settings配置远程仓库地址 4)配置maven工程的基础JDK版本 5)确认JDK环境变量配置没问题,配置maven的环境变量 验证…...

界面控件DevExpress.Drawing图形库早期增强功能分享

众所周知&#xff0c;DevExpress在v22.2发布周期中引入了全新的DevExpress.Drawing图形库&#xff08;并且已经在随后的小更新中引入了一系列增强功能&#xff09;。 在这篇博文中&#xff0c;我们将总结在DevExpress v23.1中解决的一些问题&#xff0c;以及在EAP构建中为以下…...

Semantic Kernel 入门系列:Connector连接器

当我们使用Native Function的时候&#xff0c;除了处理一些基本的逻辑操作之外&#xff0c;更多的还是需要进行外部数据源和服务的对接&#xff0c;要么是获取相关的数据&#xff0c;要么是保存输出结果。这一过程在Semantic Kernel中可以被归类为Connector。 Connector更像是…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战&#xff0c;克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具&#xff0c;支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议&#xff0c;涵盖接口测试、性能测试、数字体验监测等测试类型…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...