Vue实现电子签名功能的完整代码

 更新时间:2025年02月12日 10:36:31   作者:hhzz  
本文介绍了在Vue项目中实现电子签名功能的步骤,包括将原始文档转换为图片、使用`signature_pad`和`html2canvas`库、调整签名位置和缩放比例、合并图片等步骤,并提供了完整的源码,需要的朋友可以参考下

一、具体思路

在vue项目中使用以下步骤思路去实现:

起初的原始文档的格式都转成图片格式来处理;

电子签名的模板转成base64

前端将文档的样式和电子签名的模板合成一张图片,进行预览

通过原始图片文档与电子签名的图片进行合并,期间需要调整签名base64的位置和缩放比例,然后添加合并到原始文档,最终形成签名后的文档。

二、所需依赖

npm i signature_pad@4.2.0

npm i html2canvas

signature_pad 签名板 signature_pad - npm

html2canvas html转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   笔的颜色
    },

发现我鼠标所在的位置跟落笔产生了偏移 需要调用一下这个 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()

    },

3.1 canvas 转base64

this.signatureimgSrc =this.signatureExample.toDataURL('image/png')   //得到了就是base64的   

打印输入如下:

3.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>

到此这篇关于Vue实现电子签名功能的完整代码的文章就介绍到这了,更多相关Vue电子签名内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue结合echarts实现绘制水滴图

    Vue结合echarts实现绘制水滴图

    这篇文章主要为大家详细介绍了Vue如何结合echarts实现水滴图的绘制,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-07-07
  • vue-cli4项目开启eslint保存时自动格式问题

    vue-cli4项目开启eslint保存时自动格式问题

    这篇文章主要介绍了vue-cli4项目开启eslint保存时自动格式的问题小结,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • Vue优化大批量接口请求的实战指南

    Vue优化大批量接口请求的实战指南

    在Vue项目开发中,大批量接口请求是常见场景,本文结合Vue2/Vue3实战,从请求管控,缓存策略,代码优化和异常处理四大维度,提供可直接落地的优化方案,覆盖核心场景与避坑要点,希望对大家有所帮助
    2026-04-04
  • Vue中保持页面状态的终极方案

    Vue中保持页面状态的终极方案

    这篇文章主要探讨了在Vue中保持页面状态的问题,尤其是在单页应用(SPA)中常见的挑战,如滚动位置丢失、表单数据丢失和页面状态保存,提出了四种主要策略的相关资料
    2025-10-10
  • 关于element同时使用Drawer和Dialog出现多个遮罩问题

    关于element同时使用Drawer和Dialog出现多个遮罩问题

    这篇文章主要介绍了关于element同时使用Drawer和Dialog出现多个遮罩问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • vue循环el-button实现点击哪个按钮,那个按钮就变色

    vue循环el-button实现点击哪个按钮,那个按钮就变色

    这篇文章主要介绍了vue循环el-button实现点击哪个按钮,那个按钮就变色问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • 在Vue3中实现一个自定义的v-html组件方式

    在Vue3中实现一个自定义的v-html组件方式

    文章介绍了Vue.js中的v-html指令及其潜在的安全风险,然后通过DOMParser实现了一个自定义的v-html组件,以提高安全性并避免XSS攻击
    2025-11-11
  • 前端Vue通过Minio返回的URL下载文件实现方法

    前端Vue通过Minio返回的URL下载文件实现方法

    Minio是一个灵活、高性能、开源的对象存储解决方案,适用于各种存储需求,并可以与云计算、容器化、大数据和应用程序集成,这篇文章主要给大家介绍了关于前端Vue通过Minio返回的URL下载文件实现的相关资料,需要的朋友可以参考下
    2024-07-07
  • vue中的Canvas使用及说明

    vue中的Canvas使用及说明

    文章详细介绍了HTML5 Canvas的模板区、样式区和脚本区的使用方法,包括如何设置Canvas的宽高、水平居中、获取Canvas元素、绘制图形(直线、矩形、圆、文字)、转换为Base64、下载图片以及加载图片并添加文字水印等操作
    2025-10-10
  • three.js实现vr全景图功能实例(vue)

    three.js实现vr全景图功能实例(vue)

    去年全景图在微博上很是火爆了一阵,正好我也做过一点全景相关的项目,下面这篇文章主要给大家介绍了关于three.js实现vr全景图功能的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-05-05

最新评论