Vue實(shí)現(xiàn)電子簽名功能的完整代碼
一、具體思路
在vue項(xiàng)目中使用以下步驟思路去實(shí)現(xiàn):
起初的原始文檔的格式都轉(zhuǎn)成圖片格式來處理;
電子簽名的模板轉(zhuǎn)成base64
前端將文檔的樣式和電子簽名的模板合成一張圖片,進(jìn)行預(yù)覽
通過原始圖片文檔與電子簽名的圖片進(jìn)行合并,期間需要調(diào)整簽名base64的位置和縮放比例,然后添加合并到原始文檔,最終形成簽名后的文檔。
二、所需依賴
npm i signature_pad@4.2.0 npm i html2canvas
signature_pad 簽名板 signature_pad - npm
html2canvas html轉(zhuǎn)cavas html2canvas - npm
三、添加簽名面板
<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 筆的顏色
},

發(fā)現(xiàn)我鼠標(biāo)所在的位置跟落筆產(chǎn)生了偏移 需要調(diào)用一下這個(gè) adjustSignatureImgPos這個(gè)方法
//校正簽名位置偏移
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()
},

3.1 canvas 轉(zhuǎn)base64
this.signatureimgSrc =this.signatureExample.toDataURL('image/png') //得到了就是base64的
打印輸入如下:

3.2 電子簽名等比例縮小
把生成的電子簽名等比例縮小
傳入我們電子簽名的base64,然后生成一個(gè)新元素image ,改變它的大小,然后在通過canvas轉(zhuǎn)成base64,在return 出來
我們需要使用Promise去異步處理他,并拿到返回的新base64
// 繪制的canvas 進(jìn)行縮放并轉(zhuǎn)為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 // 縮放的倍數(shù)
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轉(zhuǎn)cavas(原始文檔)
我們需要把html編寫的文檔轉(zhuǎn)成base64
這個(gè)我們用html2canvas 這個(gè)插件:
import html2canvas from 'html2canvas'
html2canvas(document.querySelector("#capture")).then(canvas => {
this.htmlimgUrl = canvas.toDataURL("image/png"); // 將canvas轉(zhuǎn)換成img的src流
});
五、合成圖片
接下來我們需要將html文檔和電子簽名模板,合成一張圖片
寫一個(gè)合并圖片的方法:
傳入兩個(gè)參數(shù),分別是原始圖片文檔和電子簽名圖片文檔;
//合并圖片
mergeimg(imgUrl,signatureimgSrc){
// 創(chuàng)建一個(gè) canvas 元素
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 創(chuàng)建兩個(gè)圖像對象
const img1 = new Image();
const img2 = new Image();
// 設(shè)置圖像的 src 屬性為 Base64 編碼的字符串
img1.src = imgUrl;
img2.src = signatureimgSrc;
// 設(shè)置 canvas 的寬度和高度
// 這里假設(shè)我們將圖像水平排列,因此寬度是兩幅圖寬度之和,高度取最大值
canvas.width = img1.width + img2.width;
canvas.height = Math.max(img1.height, img2.height);
// 繪制第一張圖像
ctx.drawImage(img1, 0, 0);
// 繪制第二張圖像,放置在第一張圖像的右邊
ctx.drawImage(img2, 300, 500);
// 將合并后的圖像導(dǎo)出為 Base64 編碼的字符串
this.mergedImage = canvas.toDataURL('image/png');
},
調(diào)用合并圖片方法:
//點(diǎn)擊提交 進(jìn)行合并圖片 保存簽名面板內(nèi)容
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頁面內(nèi)容轉(zhuǎn)為base64
html2base64(){
html2canvas(document.querySelector("#capture")).then(canvas => {
this.htmlimgUrl = canvas.toDataURL("image/png"); // 將canvas轉(zhuǎn)換成img的src流
});
},
//合并圖片
mergeimg(imgUrl,signatureimgSrc){
// 創(chuàng)建一個(gè) canvas 元素
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 創(chuàng)建兩個(gè)圖像對象
const img1 = new Image();
const img2 = new Image();
// 設(shè)置圖像的 src 屬性為 Base64 編碼的字符串
img1.src = imgUrl;
img2.src = signatureimgSrc;
// 等待兩張圖像都加載完畢
// await Promise.all([this.loadImage(img1), this.loadImage(img2)]);
// 設(shè)置 canvas 的寬度和高度
// 這里假設(shè)我們將圖像水平排列,因此寬度是兩幅圖寬度之和,高度取最大值
canvas.width = img1.width + img2.width;
canvas.height = Math.max(img1.height, img2.height);
// 繪制第一張圖像
ctx.drawImage(img1, 0, 0);
// 繪制第二張圖像,放置在第一張圖像的右邊
ctx.drawImage(img2, 300, 500);
// 將合并后的圖像導(dǎo)出為 Base64 編碼的字符串
this.mergedImage = canvas.toDataURL('image/png');
},
loadImage(img) {
return new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
});
},
//點(diǎn)擊清除按鈕 清除簽名面板內(nèi)容
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)
},
//點(diǎn)擊提交 進(jìn)行合并圖片 保存簽名面板內(nèi)容
submit(){
this.mergeimg(this.imgUrl,this.signatureimgSrcScale)
},
// 繪制的canvas 進(jìn)行縮放并轉(zhuǎn)為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 // 縮放的倍數(shù)
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>
到此這篇關(guān)于Vue實(shí)現(xiàn)電子簽名功能的完整代碼的文章就介紹到這了,更多相關(guān)Vue電子簽名內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue結(jié)合echarts實(shí)現(xiàn)繪制水滴圖
這篇文章主要為大家詳細(xì)介紹了Vue如何結(jié)合echarts實(shí)現(xiàn)水滴圖的繪制,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-07-07
vue-cli4項(xiàng)目開啟eslint保存時(shí)自動(dòng)格式問題
這篇文章主要介紹了vue-cli4項(xiàng)目開啟eslint保存時(shí)自動(dòng)格式的問題小結(jié),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
Vue優(yōu)化大批量接口請求的實(shí)戰(zhàn)指南
在Vue項(xiàng)目開發(fā)中,大批量接口請求是常見場景,本文結(jié)合Vue2/Vue3實(shí)戰(zhàn),從請求管控,緩存策略,代碼優(yōu)化和異常處理四大維度,提供可直接落地的優(yōu)化方案,覆蓋核心場景與避坑要點(diǎn),希望對大家有所幫助2026-04-04
關(guān)于element同時(shí)使用Drawer和Dialog出現(xiàn)多個(gè)遮罩問題
這篇文章主要介紹了關(guān)于element同時(shí)使用Drawer和Dialog出現(xiàn)多個(gè)遮罩問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
vue循環(huán)el-button實(shí)現(xiàn)點(diǎn)擊哪個(gè)按鈕,那個(gè)按鈕就變色
這篇文章主要介紹了vue循環(huán)el-button實(shí)現(xiàn)點(diǎn)擊哪個(gè)按鈕,那個(gè)按鈕就變色問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
在Vue3中實(shí)現(xiàn)一個(gè)自定義的v-html組件方式
文章介紹了Vue.js中的v-html指令及其潛在的安全風(fēng)險(xiǎn),然后通過DOMParser實(shí)現(xiàn)了一個(gè)自定義的v-html組件,以提高安全性并避免XSS攻擊2025-11-11
前端Vue通過Minio返回的URL下載文件實(shí)現(xiàn)方法
Minio是一個(gè)靈活、高性能、開源的對象存儲(chǔ)解決方案,適用于各種存儲(chǔ)需求,并可以與云計(jì)算、容器化、大數(shù)據(jù)和應(yīng)用程序集成,這篇文章主要給大家介紹了關(guān)于前端Vue通過Minio返回的URL下載文件實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-07-07
three.js實(shí)現(xiàn)vr全景圖功能實(shí)例(vue)
去年全景圖在微博上很是火爆了一陣,正好我也做過一點(diǎn)全景相關(guān)的項(xiàng)目,下面這篇文章主要給大家介紹了關(guān)于three.js實(shí)現(xiàn)vr全景圖功能的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-05-05

