vue前端项目中实现电子签名功能(附完整源码)
文章目录
- 一、具体思路
- 二、所需依赖
- 三、添加签名面板
- 2.1 canvas 转base64
- 2.2 电子签名等比例缩小
- 四、html转cavas(原始文档)
- 五、合成图片
- 六、效果测试
- 七、完整源码
一、具体思路
在vue项目中使用以下步骤思路去实现:
-
起初的原始文档的格式都转成图片格式来处理;
-
电子签名的模板转成base64
-
前端将文档的样式和电子签名的模板合成一张图片,进行预览
通过原始图片文档与电子签名的图片进行合并,期间需要调整签名base64的位置和缩放比例,然后添加合并到原始文档,最终形成签名后的文档。
二、所需依赖
npm i signature_pad@4.2.0
npm i html2canvas
signature_pad 签名板
https://www.npmjs.com/package/signature_pad
html2canvas html转cavas
https://www.npmjs.com/package/html2canvas
三、添加签名面板
<div class="sign-box" style="width:300px;height:200px;padding: 10px; margin:10px;background-color: rgb(221 216 216);">
<p><span style="color: #f00;">*</span>签名版<span style="color: #f00;">*</span></p>
<div>
<canvas id="signCanvas"/>
</div>
<button type="default" @click="clear()">清除</button>
<button type="default" @click="review()">游览</button>
<button aria-placeholder="添加签名到文件" type="default" @click="submit()">提交</button>
</div>
import SignaturePad from 'signature_pad'
mounted() {
const canvas = document.getElementById('signCanvas')
this.signatureExample = new SignaturePad(canvas, { penColor: 'rgb(0, 0, 0)' }) //penColor 笔的颜色
},
发现我鼠标所在的位置跟落笔产生了偏移 需要调用一下这个 adjustSignatureImgPos这个方法
//校正签名位置偏移
adjustSignatureImgPos() {
const canvas = document.getElementById('signCanvas')
const ratio = Math.max(window.devicePixelRatio || 1, 1) // 清除画布
canvas.width = canvas.offsetWidth * ratio
canvas.height = canvas.offsetHeight * ratio
canvas.getContext('2d').scale(ratio, ratio)
},
mounted() {
const canvas = document.getElementById('signCanvas')
this.signatureExample = new SignaturePad(canvas, { penColor: 'rgb(0, 0, 0)' }) //penColor 笔的颜色
this.adjustSignatureImgPos()
},
2.1 canvas 转base64
this.signatureimgSrc =this.signatureExample.toDataURL('image/png') //得到了就是base64的
打印输入如下:
2.2 电子签名等比例缩小
把生成的电子签名等比例缩小
传入我们电子签名的base64,然后生成一个新元素image ,改变它的大小,然后在通过canvas转成base64,在return 出来
我们需要使用Promise去异步处理他,并拿到返回的新base64
// 绘制的canvas 进行缩放并转为base64
resizeImage(src) {
return new Promise((resolve) => {
const img = new Image()
img.src = src
img.onload = () => {
const originalWidth = img.width
const originalHeight = img.height
const scaleFactor = 0.5 // 缩放的倍数
const resizedWidth = originalWidth * scaleFactor
const resizedHeight = originalHeight * scaleFactor
const canvas = document.createElement('canvas')
canvas.width = resizedWidth
canvas.height = resizedHeight
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0, resizedWidth, resizedHeight)
const base64 = canvas.toDataURL('image/png')
resolve(base64)
}
})
},
效果如下:
四、html转cavas(原始文档)
我们需要把html编写的文档转成base64
这个我们用html2canvas 这个插件:
import html2canvas from 'html2canvas'
html2canvas(document.querySelector("#capture")).then(canvas => {
this.htmlimgUrl = canvas.toDataURL("image/png"); // 将canvas转换成img的src流
});
五、合成图片
接下来我们需要将html文档和电子签名模板,合成一张图片
写一个合并图片的方法:
传入两个参数,分别是原始图片文档和电子签名图片文档;
//合并图片
mergeimg(imgUrl,signatureimgSrc){
// 创建一个 canvas 元素
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 创建两个图像对象
const img1 = new Image();
const img2 = new Image();
// 设置图像的 src 属性为 Base64 编码的字符串
img1.src = imgUrl;
img2.src = signatureimgSrc;
// 设置 canvas 的宽度和高度
// 这里假设我们将图像水平排列,因此宽度是两幅图宽度之和,高度取最大值
canvas.width = img1.width + img2.width;
canvas.height = Math.max(img1.height, img2.height);
// 绘制第一张图像
ctx.drawImage(img1, 0, 0);
// 绘制第二张图像,放置在第一张图像的右边
ctx.drawImage(img2, 300, 500);
// 将合并后的图像导出为 Base64 编码的字符串
this.mergedImage = canvas.toDataURL('image/png');
},
调用合并图片方法:
//点击提交 进行合并图片 保存签名面板内容
submit(){
this.mergeimg(this.imgUrl,this.signatureimgSrcScale)
},
六、效果测试
七、完整源码
<template>
<div class="page">
<div>
<h3>原文档</h3>
<h3>-----------------------</h3>
<img :src="imgUrl">
</div>
<div class="sign-box" style="width:300px;height:200px;padding: 10px; margin:10px;background-color: rgb(221 216 216);">
<p><span style="color: #f00;">*</span>签名版<span style="color: #f00;">*</span></p>
<div>
<canvas id="signCanvas"/>
</div>
<button type="default" @click="clear()">清除</button>
<button type="default" @click="review()">游览</button>
<button aria-placeholder="添加签名到文件" type="default" @click="submit()">提交</button>
</div>
<div>
<h3>签名</h3>
<img :src="signatureimgSrcScale">
</div>
<div>
<h3>签名后的文档</h3>
<h3>-----------------------</h3>
<img :src="mergedImage">
</div>
</div>
</template>
<script>
import SignaturePad from 'signature_pad'
import html2canvas from 'html2canvas'
import pdf from 'vue-pdf'
export default {
components: {
pdf
},
data() {
return {
imgUrl: '../../static/file.png',
signatureimgSrc: null,
htmlimgUrl: null,
signatureExample: null,
mergedImage: null,
signatureimgSrcScale: null
}
},
mounted() {
const canvas = document.getElementById('signCanvas')
this.signatureExample = new SignaturePad(canvas, { penColor: 'rgb(0, 0, 0)' }) //penColor 笔的颜色
this.adjustSignatureImgPos()
},
created() {
},
methods: {
//校正签名位置偏移
adjustSignatureImgPos() {
const canvas = document.getElementById('signCanvas')
const ratio = Math.max(window.devicePixelRatio || 1, 1) // 清除画布
canvas.width = canvas.offsetWidth * ratio
canvas.height = canvas.offsetHeight * ratio
canvas.getContext('2d').scale(ratio, ratio)
},
//html页面内容转为base64
html2base64(){
html2canvas(document.querySelector("#capture")).then(canvas => {
this.htmlimgUrl = canvas.toDataURL("image/png"); // 将canvas转换成img的src流
});
},
//合并图片
mergeimg(imgUrl,signatureimgSrc){
// 创建一个 canvas 元素
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 创建两个图像对象
const img1 = new Image();
const img2 = new Image();
// 设置图像的 src 属性为 Base64 编码的字符串
img1.src = imgUrl;
img2.src = signatureimgSrc;
// 等待两张图像都加载完毕
// await Promise.all([this.loadImage(img1), this.loadImage(img2)]);
// 设置 canvas 的宽度和高度
// 这里假设我们将图像水平排列,因此宽度是两幅图宽度之和,高度取最大值
canvas.width = img1.width + img2.width;
canvas.height = Math.max(img1.height, img2.height);
// 绘制第一张图像
ctx.drawImage(img1, 0, 0);
// 绘制第二张图像,放置在第一张图像的右边
ctx.drawImage(img2, 300, 500);
// 将合并后的图像导出为 Base64 编码的字符串
this.mergedImage = canvas.toDataURL('image/png');
},
loadImage(img) {
return new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
});
},
//点击清除按钮 清除签名面板内容
clear(){
//清除签名面板的方法
this.signatureExample.clear()
},
async review(){
this.signatureimgSrc =this.signatureExample.toDataURL('image/png')
this.signatureimgSrcScale =await this.resizeImage(this.signatureExample.toDataURL('image/png'))
console.log("======== this.signatureimgSrc==========", this.signatureimgSrc)
},
//点击提交 进行合并图片 保存签名面板内容
submit(){
this.mergeimg(this.imgUrl,this.signatureimgSrcScale)
},
// 绘制的canvas 进行缩放并转为base64
resizeImage(src) {
return new Promise((resolve) => {
const img = new Image()
img.src = src
img.onload = () => {
const originalWidth = img.width
const originalHeight = img.height
const scaleFactor = 0.5 // 缩放的倍数
const resizedWidth = originalWidth * scaleFactor
const resizedHeight = originalHeight * scaleFactor
const canvas = document.createElement('canvas')
canvas.width = resizedWidth
canvas.height = resizedHeight
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0, resizedWidth, resizedHeight)
const base64 = canvas.toDataURL('image/png')
resolve(base64)
}
})
},
}
}
</script>
<style>
.page{
display: flex;
flex-direction: row;
margin: 10px;
text-align: center;
background: #fff;
padding: 10px;
}
</style>
上述代码只是平常练习使用,如需使用在正式项目中,请自行修改完善!!!
人生从来没有真正的绝境。只要一个人的心中还怀着一粒信念的种子,那么总有一天,他就能走出困境,让生命重新开花结果。