vue实现电子签名、图片合成、及预览功能
业务功能:电子签名、图片合成、及预览功能
业务背景:需求说想要实现一个电子签名,然后需要提供一个预览的功能,可以查看签完名之后的完整效果。
需求探讨:后端大佬跟我说,文档我返回给你一个PDF的oss链接,然后你直接展示,你前端签完名之后给我一个base64的字符串就可以了,我回复ojbk(草率的一批)。
等我转过身,调研一下技术实现发现不对,我大前端需要做一个预览的功能啊,它需要将多张图片合成一张图片,你给我一个oss链接,我怎么转base64。带着这个想法,我跟我们的后端大佬探讨了一下,
最终确定方案是这样:
1.文档的样式由Css+Html去画
2.电子签名的模板转成base64给后端保持不变
3.前端将文档的样式和电子签名的模板合成一张图片,进行预览
根据以上,作为一名码农,我翻阅了,github,npm 找到了符合本次功能的插件。
插件:
signature_pad 签名板 链接: https://www.npmjs.com/package/signature_pad
merge-images 合并图像 链接: https://www.npmjs.com/package/merge-images
html2canvas html转cavas 链接:https://www.npmjs.com/package/html2canvas
先放效果图:

文档的css+html的样式,我就不献丑了哈,大家按自己的理解来。
电子签名的画板
- html中创建一个id为signCanvas的canvas元素
<section><div class="sign-box"><p><span style="color: #f00;">*</span>{{ $t('本人签名') }}</p><el-button type="default" @click="clear(1)">{{ $t('清空') }}</el-button></div><canvas id="signCanvas" style="width:100%;height:300px;" />
</section>
- 初始化 SignaturePad
mounted(){const canvas = document.getElementById('signCanvas')this.signatureExample = new SignaturePad(canvas, { penColor: 'rgb(0, 0, 0)' }) //penColor 笔的颜色
}
然后我就尝试了一下,发现我鼠标所在的位置跟落笔产生了偏移

然后翻阅文档发现,是需要调用一下这个 resizeCanvas 这个方法

mounted(){const canvas = document.getElementById('signCanvas')this.signatureExample = new SignaturePad(canvas, { penColor: 'rgb(0, 0, 0)' }) //penColor 笔的颜色 this.resizeCanvas()
}resizeCanvas() {const canvas = document.getElementById('signCanvas')const ratio = Math.max(window.devicePixelRatio || 1, 1) // 清除画布canvas.width = canvas.offsetWidth * ratiocanvas.height = canvas.offsetHeight * ratiocanvas.getContext('2d').scale(ratio, ratio)this.signatureExample.clear()
},
调用了一下,果然有用。
再加一个清除的方法,官方有提供,直接调用即可
this.signatureExample.clear()
canvas 转base64
this.signatureExample.toDataURL('image/png') //得到了就是base64的 data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfgAAAL2CAYAAA......
html转cavas
- 我们需要把html编写的文档转成base64
这个我们用html2canvas 这个插件就可以了
const element = document.querySelector('.html_body') // 需要导出的页面 哪个元素需要转成cavas 就获取它
const htmlCanvas = await html2canvas(element, {
allowTaint: true,
useCORS: true
})
this.imgSrc = htmlCanvas.toDataURL('image/png') //得到base64
合成图片
- 接下来我们需要将html文档和电子签名模板,合成一张图片
import mergeImages from 'merge-images'
/* x 、y 是图片在合成图片上的位置(可自行调整) */
const mergeImagesList = [{ src: this.imgSrc, x: 0, y: 0 }, //html 转成的base64{ src: this.signatureExample.toDataURL('image/png'), x: 370, y: 440 } // 电子签名的 base64
]
mergeImages(mergeImagesList).then(b64 => {this.previewSrc = b64 //返回base64 可直接用于展示
})
这个时候我们看预览的结果发现,电子签名的字好大啊,这是跟我们canvas元素的大小是有关系的,如果我们改变了这个元素的大小,用户签名的时候就会体验感很差,这肯定不符合我们的预期,所以我们要把生成的电子签名等比例缩小。

通过这个方法我们可以传入我们电子签名的base64,然后生成一个新元素image ,改变它的大小,然后在通过canvas转成base64,在return 出来
PS:我们需要使用Promise去异步处理他,并拿到返回的新base64.
// 绘制的canvas 进行缩放并转为base64resizeImage(src) {return new Promise((resolve) => {const img = new Image()img.src = srcimg.onload = () => {const originalWidth = img.widthconst originalHeight = img.heightconst scaleFactor = 0.3 // 缩放的倍数const resizedWidth = originalWidth * scaleFactorconst resizedHeight = originalHeight * scaleFactorconst canvas = document.createElement('canvas')canvas.width = resizedWidthcanvas.height = resizedHeightconst ctx = canvas.getContext('2d')ctx.drawImage(img, 0, 0, resizedWidth, resizedHeight)const base64 = canvas.toDataURL('image/png')resolve(base64)}})},
然后我们重新改下合成图片的方法
import mergeImages from 'merge-images'
const imgStr = await this.resizeImage(this.signatureExample.toDataURL('image/png'))
const mergeImagesList = [{ src: this.imgSrc, x: 0, y: 0 }, //html 转成的base64{ src: imgStr, x: 370, y: 440 } // 电子签名的 base64
]
mergeImages(mergeImagesList).then(b64 => {this.previewSrc = b64 //返回base64 可直接用于展示
})
ok,没问题了

还有一点需要注意的是,后端是不需要data:image/png;base64,所以还我们需要对这个字符串做个处理
// 获取到image的base64 可以把这个传到后台解析成图片
// imgStr = data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfgAAAL2CAYAAA......
// 去掉data:image/png;base64,我们只要后面的部分iVBORw0KGgoAAAANSUhEUgAAAfgAAAL2CAYAAA......
const subStr = (str) => {return str.substring(22, str.length)
}
subStr(this.signatureExample.toDataURL('image/png')) //返回的就可以直接传给后端啦
总结
本文介绍了电子签名、图片合成、及预览功能,大体的整块逻辑及代码已提供,细节方面需要大家,自行调试哈。
相关文章:
vue实现电子签名、图片合成、及预览功能
业务功能:电子签名、图片合成、及预览功能 业务背景:需求说想要实现一个电子签名,然后需要提供一个预览的功能,可以查看签完名之后的完整效果。 需求探讨:后端大佬跟我说,文档我返回给你一个PDF的oss链接…...
【flink】之如何消费kafka数据?
为了编写一个使用Apache Flink来读取Apache Kafka消息的示例,我们需要确保我们的环境已经安装了Flink和Kafka,并且它们都能正常运行。此外,我们还需要在项目中引入相应的依赖库。以下是一个详细的步骤指南,包括依赖添加、代码编写…...
科研绘图系列:R语言山脊图(Ridgeline Chart)
介绍 山脊图(Ridge Chart)是一种用于展示数据分布和比较不同类别或组之间差异的数据可视化技术。它通常用于展示多个维度或变量之间的关系,以及它们在不同组中的分布情况。山脊图的特点: 多变量展示:山脊图可以同时展示多个变量的分布情况,允许用户比较不同变量之间的关…...
Boost搜索引擎:如何建立 用户搜索内容 与 网页文件内容 之间的关系
如果想使“用户搜索内容”和“网页文件内容”之间产生联系,就应该将“用户搜索内容”和“网页文件”分为很小的单元 (这个单元就是关键词),寻找用户搜索单元是否出现在这个文档之中,如果出现就证明这个网页文件和用户搜…...
【QT】QT 窗口(菜单栏、工具栏、状态栏、浮动窗口、对话框)
Qt 窗口是通过 QMainWindow类来实现的。 QMainWindow 是一个为用户提供主窗口程序的类,继承自 QWidget 类,并且提供了⼀个预定义的布局。QMainWindow 包含一个菜单栏(Menu Bar)、多个工具栏(Tool Bars)、…...
Golang | Leetcode Golang题解之第283题移动零
题目: 题解: func moveZeroes(nums []int) {left, right, n : 0, 0, len(nums)for right < n {if nums[right] ! 0 {nums[left], nums[right] nums[right], nums[left]left}right} }...
ubuntu22.04 安装 NVIDIA 驱动以及CUDA
目录 1、事前问题解决 2、安装 nvidia 驱动 3、卸载 nvidia 驱动方法 4、安装 CUDA 5、安装 Anaconda 6、安装 PyTorch 1、事前问题解决 在安装完ubuntu之后,如果进入ubuntu出现黑屏情况,一般就是nvidia驱动与linux自带的不兼容,可以通…...
数据结构·AVL树
1. AVL树的概念 二叉搜索树虽可以缩短查找的效率,但如果存数据时接近有序,二叉搜索将退化为单支树,此时查找元素效率相当于在顺序表中查找,效率低下。因此两位俄罗斯数学家 G.M.Adelson-Velskii 和E.M.Landis 在1962年发明了一种解…...
记一次Mycat分库分表实践
接了个活,又搞分库分表。 一、分库分表 在系统的研发过程中,随着数据量的不断增长,单库单表已无法满足数据的存储需求,此时就需要对数据库进行分库分表操作。 分库分表是随着业务的不断发展,单库单表无法承载整体的数据存储时,采取的一种将整体数据分散存储到不同服务…...
数据分析:微生物数据的荟萃分析框架
介绍 Meta-analysis of fecal metagenomes reveals global microbial signatures that are specific for colorectal cancer提供了一种荟萃分析的框架,它主要基于常用的Wilcoxon rank-sum test和Blocked Wilcoxon rank-sum test 方法计算显著性,再使用分…...
Django—admin后台管理
Django官网 https://www.djangoproject.com/ 如果已经有了Django跳过这步 安装Django: 如果你还没有安装Django,可以通过Python的包管理器pip来安装: pip install django 创建项目: 使用Django创建一个新的项目: …...
数字图像处理中的常用特殊矩阵及MATLAB应用
一、前言 Matlab的名称来源于“矩阵实验室(Matrix Laboratory)”,其对矩阵的操作具有先天性的优势(特别是相对于C语言的数组来说)。在数字图像处理中,为了提高编程效率,我们可以使用多种方式来创…...
vue侦听器(Watch)精彩案例剖析一
目录 watch介绍 监视普通数据类型 监视对象类型 watch介绍 在 Vue 中,watch主要用于监视数据的变化,并执行相应操作。一旦被监视的属性发生变化,回调函数将自动被触发。当在 Vue 中使用watch来响应数据变化时,首先要清楚,watch本质上是一个对象,且必须以对象的…...
HTTP 协议浅析
HTTP(HyperText Transfer Protocol,超文本传输协议)是应用层最重要的协议之一。它定义了客户端和服务器之间的数据传输方式,并成为万维网(World Wide Web)的基石。本文将深入解析 HTTP 协议的基础知识、工作…...
VsCode | 让空文件夹始终展开不折叠
文章目录 1 问题引入2 解决办法3 效果展示 1 问题引入 可能很多小伙伴更新VsCode或者下载新版本时候 ,创建的文件 会出现xxx文件夹/xxx文件夹,看着很不舒服,所以该如何展开所有空文件夹呢? 2 解决办法 找到VsCode的设置 &…...
Centos7_Minimal安装Cannot find a valid baseurl for repo: base/7/x86_6
问题 运行yum报此问题 就是没网 解决方法 修改网络信息配置文件,打开配置文件,输入命令: vi /etc/sysconfig/network-scripts/ifcfg-网卡名字把ONBOOTno,改为ONBOOTyes 重启网卡 /etc/init.d/network restart 网路通了...
Spark_Oracle_II_Spark高效处理Oracle时间数据:通过JDBC桥接大数据与数据库的分析之旅
接前文背景, 当需要从关系型数据库(如Oracle)中读取数据时,Spark提供了JDBC连接功能,允许我们轻松地将数据从Oracle等数据库导入到Spark DataFrame中。然而,在处理时间字段时,可能会遇到一些挑战…...
力扣 459重复的子字符串
思路: KMP算法的核心是求next数组 next数组代表的是当前字符串最大前后缀的长度 而求重复的子字符串就是求字符串的最大前缀与最大后缀之间的子字符串 如果这个子字符串是字符串长度的约数,则true /** lc appleetcode.cn id459 langcpp** [459] 重复…...
MyBatis XML配置文件
目录 一、引入依赖 二、配置数据库的连接信息 三、实现持久层代码 3.1 添加mapper接口 3.2 添加UserInfoXMLMapper.xml 3.3 增删改查操作 3.3.1 增(insert) 3.3.2 删(delete) 3.3.3 改(update) 3.3.4 查(select) 本篇内容仍然衔接上篇内容,使用的代码及案…...
读写RDS或RData等不同格式的文件,包括CSV和TXT、Excel的常见文件格式,和SPSS、SAS、Stata、Minitab等统计软件的数据文件
R语言是数据分析和科学计算的强大工具,其丰富的函数和包使得处理各种数据格式变得相对简单。在本文中,我们将详细介绍如何使用R语言的函数命令读取和写入不同格式的文件,包括RDS或RData格式文件、常见的文本文件(如CSV和TXT)、Excel文件,和和SPSS、SAS、Stata、Minitab等…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...
ui框架-文件列表展示
ui框架-文件列表展示 介绍 UI框架的文件列表展示组件,可以展示文件夹,支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项,适用于文件管理、文件上传等场景。 功能特性 支持列表模式和网格模式的切换展示支持文件和文件夹的层…...
pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决
问题: pgsql数据库通过备份数据库文件进行还原时,如果表中有自增序列,还原后可能会出现重复的序列,此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”,…...
