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

news2024/11/14 21:56:58

文章目录

    • 先实现正常的效果
    • 实现下载图片
    • 改变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">
			<img
				src="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.rotate
      let x, y
      const type = rotate / 90 % 4
      img.setAttribute("crossOrigin", 'Anonymous')
      img.onload = function () { //图片加载完,再draw 和 toDataURL
        let width = img.width
        let height = img.height
        const 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 = width
            width = height
            height = blg
          }
        } else if (type == 2) {
          x = width;
          y = height;
        } else if (type == 3) {
          x = 0;
          y = height;
          if (!flg) {
            const blg = width
            width = height
            height = blg
          }
        }
        canvas.width = width
        canvas.height = height
        let ctx = canvas.getContext("2d")
        ctx.translate(x, y);
        ctx.rotate(rotate * Math.PI / 180);
        ctx.drawImage(img, 0, 0, img.width, img.height);
        //图片转base64
        base64 = 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挂载的dom
        var 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 names
          options.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样式就行了,或者直接用行内style
          addClass(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后会自动使用我们写的补丁
在这里插入图片描述

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

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

相关文章

计算机视觉与图形学-神经渲染专题-Seal-3D(基于NeRF的像素级交互式编辑)

摘要 随着隐式神经表示或神经辐射场 (NeRF) 的流行&#xff0c;迫切需要与隐式 3D 模型交互的编辑方法&#xff0c;以完成后处理重建场景和 3D 内容创建等任务。虽然之前的作品从不同角度探索了 NeRF 编辑&#xff0c;但它们在编辑灵活性、质量和速度方面受到限制&#xff0c;无…

【CI/CD】图解六种分支管理模型

图解六种分支管理模型 任何一家公司乃至于一个小组织&#xff0c;只要有写代码的地方&#xff0c;就有代码版本管理的主场&#xff0c;初入职场&#xff0c;总会遇到第一个拦路虎 git 管理流程&#xff0c;但是每一个企业似乎都有自己的 git 管理流程&#xff0c;倘若我们能掌握…

深度学习之双线性插值

1、单线性插值 单线性插值是一种用于估计两个已知数据点之间未知点的方法。它基于线性关系&#xff0c;通过计算目标位置的值&#xff0c;使用已知点之间的线性函数进行插值。这在图像处理中常用于放缩、旋转等操作&#xff0c;计算简单&#xff0c;产生平滑结果&#xff0c;但…

zookeeper集群和kafka的相关概念就部署

目录 一、Zookeeper概述 1、Zookeeper 定义 2、Zookeeper 工作机制 3、Zookeeper 特点 4、Zookeeper 数据结构 5、Zookeeper 应用场景 &#xff08;1&#xff09;统一命名服务 &#xff08;2&#xff09;统一配置管理 &#xff08;3&#xff09;统一集群管理 &#xff08;4&a…

关于openwrt的802.11w 管理帧保护使用

目录 关于openwrt的802.11w 管理帧保护使用先来看看界面上的提示 实际遇到的问题总结 关于openwrt的802.11w 管理帧保护使用 先来看看界面上的提示 注意&#xff1a;有些无线驱动程序不完全支持 802.11w。例如&#xff1a;mwlwifi 可能会有一些问题 实际遇到的问题 802.11w 管…

如何维护你的电脑:打造IT人的重要武器

文章目录 方向一&#xff1a;介绍我的电脑方向二&#xff1a;介绍我的日常维护措施1. 定期清理和优化2. 保持良好的上网习惯和安全防护3. 合理安排软件和硬件的使用4. 数据备份和系统还原 方向三&#xff1a;推荐的维护技巧1. 数据分区和多系统安装2. 内部清洁和散热优化3. 安全…

vue2商城项目-01-总结

1.配置相关 1.1eslint关闭 说明&#xff1a;根目录创建vue.config.js module.exports {// 关闭eslintlintOnSave: false,};1.2src配置别名 说明&#xff1a;根目录创建jsconfig.json文件 {"compilerOptions": {"baseUrl": "./","path…

通俗理解大模型的各大微调方法:从LoRA、QLoRA到P-Tuning V1/V2

前言 PEFT 方法仅微调少量(额外)模型参数&#xff0c;同时冻结预训练 LLM 的大部分参数 第一部分 高效参数微调的发展史 1.1 Google之Adapter Tuning&#xff1a;嵌入在transformer里 原有参数不变 只微调新增的Adapter 谷歌的研究人员首次在论文《Parameter-Efficient Tran…

代码随想录算法训练营day25 | 216. 组合总和 III,17. 电话号码的字母组合

目录 216. 组合总和 III 17. 电话号码的字母组合 216. 组合总和 III 难度&#xff1a;medium 类型&#xff1a;回溯 思路&#xff1a; 与77组合类似的题目。 代码随想录算法训练营day24 | 回溯问题&#xff0c;77. 组合_Chamberlain T的博客-CSDN博客 注意两处剪枝。 代码…

__attribute__((noreturn))

GNU C 的一大特色就是__attribute__ 机制&#xff0c;__attribute__ 可以设置函数属性&#xff08;Function Attribute&#xff09;、变量属性&#xff08;Variable Attribute&#xff09;和类型属性&#xff08;Type Attribute&#xff09;。 语法格式为: __attribute__((att…

gin框架学习

文章目录 配置go环境实现一个简单的web响应服务验证功能gin增加页面以及传递数据 配置go环境 去go官网下载对应的版本 go下载地址 tar -C /usr/local -xzf go1.4.linux-amd64.tar.gz 我们可以编辑 ~/.bash_profile 或者 /etc/profile&#xff0c;并将以下命令添加该文件的末…

linux安装Tomcat部署jpress教程

yum在线安装&#xff1a; 查看tomcat相关的安装包&#xff1a; [rootRHCE ~]# yum list | grep -i tomcat tomcat.noarch 7.0.76-16.el7_9 updates tomcat-el-2.2-api.noarch 7.0.76-16.el7_9 updat…

CI/CD—Docker初入门学习

1 docker 了解 1 Docker 简介 Docker 是基于 Go 语言的开源应用容器虚拟化技术。Docker的主要目标是build、ship and run any app&#xff0c;anywhere&#xff0c;即通过对应用组件的封装、分发、部署、运行等生命周期的管理&#xff0c;达到应用组件级别的一次封装、到处运…

综合与新综合与新型交通发展趋势[75页PPT]

导读&#xff1a;原文《综合与新综合与新型交通发展趋势[75页PPT]》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 完整版领取方式 完整版领取方式&#xff1a; 如需…

程序员副业指南:怎样实现年入10w+的目标?

大家好&#xff0c;这里是程序员晚枫&#xff0c;全网同名。 今天给大家分享一个大家都感兴趣的话题&#xff1a;程序员可以做什么副业&#xff0c;年入十万&#xff1f; 01 推荐 程序员可以从事以下副业&#xff0c;以获得一年收入10w&#xff1a; 兼职编程&#xff1a;可…

Element的Dialog+Form使用问题

在Element的Dialog中使用表单时&#xff0c;可能会出现以下问题 无法清空表单校验 <el-dialog title"新建资产" :visible.sync"addAssetsFormVisible" close"resetForm(addAssets)">resetForm (formName) {this.$refs[formName].resetFie…

推荐几款主流的Css Reset

CSS Reset CSS Reset&#xff08;CSS重置&#xff09;是一种技术&#xff0c;用于消除不同浏览器之间默认样式的差异&#xff0c;以确保网页在各个浏览器中的显示一致性。由于不同浏览器对元素的默认样式有所不同&#xff0c;使用CSS Reset可以将这些默认样式归零或统一&#x…

idea添加翻译插件并配置有道翻译

1、安装Translation插件 2、 创建有道云应用 有道智云控制台 3、设置idea 4、效果&#xff08;选中文本右键翻译&#xff0c;默认快捷键CtrlShiftY&#xff09;

OpenCV学习笔记--以车道线检测入门

本笔记gitee源代码&#xff1a; https://gitee.com/hongtao-jiang/opencv_lanedetect.git 2023.8.5 文章目录 1、OpenCV安装2、图片的读入、保存3、Canny算法边缘检测4、ROI mask5、霍夫变换6、离群值过滤7、最小二乘拟合8、直线绘制9、视频流读写 1、OpenCV安装 conda管理虚…

如何用限制立方样条(RCS)做生存分析?

一、引言 在医学和统计学领域&#xff0c;生存分析是一种分析个体生命长度和生存时间的重要方法。了解人们生存的期限和影响因素&#xff0c;对于制定健康政策、优化医疗资源的分配以及个体护理方案的制定都至关重要。传统的生存分析方法如Kaplan-Meier曲线和Cox比例风险模型已…