vue实践:构建高效的电子签名功能

news2024/11/18 1:48:05

前言

在现代数字化时代,电子签名成为了一种方便、高效且安全的签署文件的方式。本文将介绍电子签名的原理和实现方法,帮助你快速掌握这一重要的工具。


电子签名是什么?

电子签名是一种数字化的签名方式,用于验证和确认电子文档、合同或其他电子信息的真实性和完整性。它是传统纸质签名的数字化替代品,具有更高的效率、便捷性和安全性。大家看下面这个例子就会一目了然了。


一、手动实现一个简单的电子签名

下面的示例中,我们使用了 Canvas 元素来绘制用户的签名。当用户按下鼠标左键时,startDrawing 方法会被调用,开始绘制路径。当用户移动鼠标时,draw 方法会被调用,绘制路径。当用户释放鼠标左键时,stopDrawing 方法会被调用,停止绘制路径。点击"清除"按钮会清除 Canvas 上的内容,点击"保存"按钮会将签名保存为 Base64 编码的图片数据。

<template>
  <div>
    <canvas ref="canvas" @mousedown="startDrawing" @mousemove="draw" @mouseup="stopDrawing"></canvas>
    <div>
      <button @click="clearCanvas">清除</button>
      <button @click="saveSignature">保存</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isDrawing: false, // 是否正在绘制
      context: null, // Canvas上下文
    };
  },
  mounted() {
    this.context = this.$refs.canvas.getContext("2d"); // 获取Canvas上下文
    this.$refs.canvas.width = 500; // 设置Canvas的宽度
    this.$refs.canvas.height = 300; // 设置Canvas的高
  },
  methods: {
    // 鼠标按下时触发
    startDrawing(event) {
      this.isDrawing = true; // 开始绘制
      const { offsetX, offsetY } = event; // 获取鼠标相对于Canvas的偏移量
      this.context.beginPath(); // 开始新的路径
      this.context.moveTo(offsetX, offsetY); // 将路径移动到鼠标位置
    },
    // 当鼠标在 Canvas 上移动时触发
    draw(event) {
      if (!this.isDrawing) return; // 如果没有在绘制中,则返回
      const { offsetX, offsetY } = event; // 获取鼠标相对于Canvas的偏移量
      this.context.lineTo(offsetX, offsetY); // 绘制路径
      this.context.stroke(); // 绘制路径的边框
    },
    // 当鼠标松开时触发,用于停止绘制签名
    stopDrawing() {
      this.isDrawing = false; // 停止绘制
    },
    // 清除
    clearCanvas() {
      this.context.clearRect(
        0,
        0,
        this.$refs.canvas.width,
        this.$refs.canvas.height
      ); // 清除Canvas上的内容
    },
    // 保存
    saveSignature() {
      const dataURL = this.$refs.canvas.toDataURL(); // 获取签名图片的Base64编码
      // 在这里可以将dataURL发送到服务器保存,或者进行其他操作
      console.log(dataURL); // 输出签名的Base64编码到控制台
    },
  },
};
</script>
<style scoped>
canvas {
  border: 1px solid red;
}
</style>

实现效果
在这里插入图片描述


二、vue-esign 插件

vue-esign 是一个基于 vue.js 框架的电子签名组件库,它提供了一套现成的 UI 组件,包括签名面板、工具栏等,可以方便地在 vue.js 应用中实现电子签名功能。

2.1 安装

npm install vue-esign --save

2.2 引用

  • 全局引用

    import vueEsign from 'vue-esign'
    Vue.use(vueEsign)
    
  • 局部引用

    import vueEsign from 'vue-esign'
    components: { vueEsign }
    

2.3 使用

<template>
  <div class="box">
    <vue-esign ref="esign" :width="300" :height="150" :isCrop="isCrop" :lineWidth="lineWidth" :lineColor="lineColor"
      :bgColor.sync="bgColor" />
    <div>
      <button @click="handleReset">清空画板</button>
      <button @click="handleGenerate">生成图片</button>
    </div>
    <img :src="resultImg" alt="">
  </div>
</template>

<script>
import vueEsign from "vue-esign";
export default {
  components: { vueEsign },
  data() {
    return {
      lineWidth: 6,
      lineColor: "#000000",
      bgColor: "",
      resultImg: "",
      isCrop: false,
    };
  },
  mounted() {},
  methods: {
    handleReset() {
      this.$refs.esign.reset();
    },
    handleGenerate() {
      this.$refs.esign
        .generate({ format: "png", quality: 0.8 })
        .then((res) => {
          this.resultImg = res;
        })
        .catch((err) => {
          console.error(err);
          alert("生成图片失败:" + err.message);
        });
    },
  },
};
</script>
<style scoped>
.box {
  width: 300px;
  height: 150px;
  border: 1px solid red;
}
</style>

实现效果

在这里插入图片描述

此外,vue-esign 插件还支持画笔粗细自定义、画笔颜色自定义等等,感兴趣的同学可以看下面的属性试试。

2.4 常用的属性

属性类型默认值描述
widthNumber800画布宽度,即导出图片的宽度
heightNumber300画布高度,即导出图片的高度
lineWidthNumber4画笔粗细
lineColorString#000000画笔颜色
bgColorString画布背景色,为空时画布背景透明,支持多种格式 ‘#ccc’,‘#E5A1A1’,‘rgb(229, 161, 161)’,‘rgba(0,0,0,.6)’,‘red’
isCropBooleanfalse是否裁剪,在画布设定尺寸基础上裁掉四周空白部分
isClearBgColorBooleantrue清空画布时(reset)是否同时清空设置的背景色(bgColor)
formatStringimage/png生成图片格式 image/jpeg(jpg格式下生成的图片透明背景会变黑色请慎用或指定背景色)、 image/webp
qualityNumber1生成图片质量;在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。

三、vue-signature-pad 插件

vue-signature-pad 同样是一个基于 vue.js 框架的电子签名组件库,它提供了一个可定制的签名面板,可以方便地在 vue.js 应用中实现电子签名功能。

注意: 如果仍在使用 vue2 ,请安装 2.0.5 版本,对于 vue3,可以安装最新的发布版本。

3.1 安装

npm i vue-signature-pad@2.0.5

3.2 全局引用

import VueSignature from "vue-signature-pad";
Vue.use(VueSignature);

3.3 使用

<template>
  <div class="box">
    <VueSignaturePad width="300px" height="150px" ref="signaturePad" />
    <div>
      <button @click="save">保存</button>
      <button @click="undo">撤销</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {};
  },
  mounted() {},
  methods: {
    // 撤销操作
    undo() {
      this.$refs.signaturePad.undoSignature();
    },
    // 保存操作
    save() {
      const { isEmpty, data } = this.$refs.signaturePad.saveSignature();
      console.log(data);
    },
  },
};
</script>
<style scoped>
.box {
  width: 300px;
  height: 150px;
  border: 1px solid red;
}
</style>

实现效果

在这里插入图片描述

3.4 常用的属性

属性类型默认值描述
widthString100%画布宽度
heightString100%画布高度
optionsObject{}设置画笔的选项,包括线条颜色、宽度、透明度等
imagesArray[]设置背景图片。可以是 [‘1.png’, ‘2.png’] 或者 [{ src: ‘1.png’, x: 0, y: 0 }, { src: ‘2.png’, x: 0, y: 10 }]
customStyleObject{}设置画布的自定义样式。可以是一个CSS样式对象
scaleToDevicePixelRatioBooleantrue设置是否将画布缩放到设备像素比

3.5 常用的方法

方法参数描述
saveSignature(type, encoderOptions)(String, Number)保存当前的签名,并返回一个包含签名是否为空和签名数据的对象
undoSignature()-撤销上一步的签名操作
clearSignature()-清除当前的签名
mergeImageAndSignature(signature)Object 或者 String将指定的背景图片与当前的签名合并
addImages(images)Array添加多个背景图片
lockSignaturePad()-锁定签名画布,禁止用户进行签名操作
openSignaturePad()-解锁签名画布,允许用户进行签名操作
getPropImagesAndCacheImages()-获取图像的信息
clearCacheImages()-清除缓存的图片
fromDataURL(data, options, callback)(String, Object, Callback)从指定的DataURL加载签名数据
fromData(data)String从指定的数据加载签名数据
toData()-获取当前的签名数据
isEmpty()-检查当前的签名是否为空

完整功能代码

<template>
  <div class="box">
    <!-- VueSignaturePad 组件 -->
    <VueSignaturePad :width="width" :height="height" :options="options" :images="images" :customStyle="customStyle"
      :scaleToDevicePixelRatio="scaleToDevicePixelRatio" ref="signaturePad" />
    <div>
      <!-- 操作按钮 -->
      <button @click="save">保存</button>
      <button @click="undo">撤销</button>
      <button @click="clear">清除</button>
      <button @click="mergeImage">合并图片</button>
      <button @click="addImages">添加图片</button>
      <button @click="lock">锁定</button>
      <button @click="unlock">解锁</button>
      <button @click="getData">获取数据</button>
      <button @click="isEmpty">是否为空</button>
      <button @click="getPropImagesAndCacheImages">获取并缓存背景图片</button>
      <button @click="clearCacheImages">清除缓存的背景图片</button>
      <button @click="loadSignatureFromDataURL">从 Data URL 加载签名</button>
      <button @click="loadSignatureFromData">从 JSON 数据加载签名</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      width: "1000px",
      height: "500px",
      options: {
        minWidth: 2, // 画笔最小宽度
        maxWidth: 5, // 画笔最大宽度
        throttle: 16, // 画笔移动事件的时间间隔,单位为毫秒
        minDistance: 5, // 画笔移动的最小距离,单位为像素
        backgroundColor: "rgba(255, 255, 255, 0)", // 画布背景颜色
        penColor: "red", // 画笔颜色
        velocityFilterWeight: 0.7, // 画笔速度过滤器的权重
        onBegin: () => {}, // 开始签名时的回调函数
        onEnd: () => {}, // 结束签名时的回调函数
      },
      images: [],
      customStyle: {
        signatureCanvas: {
          border: "1px solid #ccc", // 画布边框
          borderRadius: "5px", // 画布圆角
        },
        signaturePad: {
          boxShadow: "0 0 5px rgba(0, 0, 0, 0.1)", // 画笔阴影
        },
      },
      scaleToDevicePixelRatio: true,
    };
  },
  methods: {
    // 保存签名
    save() {
      const { isEmpty, data } = this.$refs.signaturePad.saveSignature();
      console.log("签名是否为空:", isEmpty);
      console.log("签名数据:", data);
    },
    // 撤销签名
    undo() {
      this.$refs.signaturePad.undoSignature();
    },
    // 清除签名
    clear() {
      this.$refs.signaturePad.clearSignature();
    },
    // 合并图片和签名
    mergeImage() {
      const image = "image.jpg"; // 替换为你的背景图片路径
      this.$refs.signaturePad.mergeImageAndSignature(image);
    },
    // 添加图片
    addImages() {
      const images = ["image.jpg", "image.jpg"]; // 替换为你的背景图片路径数组
      this.$refs.signaturePad.addImages(images);
    },
    // 锁定签名画布
    lock() {
      this.$refs.signaturePad.lockSignaturePad();
    },
    // 解锁签名画布
    unlock() {
      this.$refs.signaturePad.openSignaturePad();
    },
    // 获取签名数据
    getData() {
      const data = this.$refs.signaturePad.toData();
      console.log("获取签名数据:", data);
    },
    // 检查签名是否为空
    isEmpty() {
      const empty = this.$refs.signaturePad.isEmpty();
      if (!empty) {
        alert("画布不为空!");
      } else {
        alert("画布为空!");
      }
    },
    // 获取并缓存背景图片
    getPropImagesAndCacheImages() {
      const images = ["image1.jpg", "image2.jpg"]; // 替换为你的背景图片路径数组
      this.$refs.signaturePad.getPropImagesAndCacheImages(images);
    },
    // 清除缓存的背景图片
    clearCacheImages() {
      this.$refs.signaturePad.clearCacheImages();
    },
    // 从 Data URL 加载签名
    loadSignatureFromDataURL() {
      const dataURL = "data:image/png;base64,iVBORw0KG..."; // 替换为你的 Data URL
      this.$refs.signaturePad.fromDataURL(dataURL);
    },
    // 从 JSON 数据加载签名
    loadSignatureFromData() {
      const signatureData = {
        width: 500,
        height: 500,
        data: [
          {
            color: "#000",
            points: [
              { x: 100, y: 100 },
              { x: 200, y: 200 },
              { x: 300, y: 300 },
            ],
          },
          {
            color: "#f00",
            points: [
              { x: 400, y: 400 },
              { x: 450, y: 450 },
              { x: 500, y: 500 },
            ],
          },
        ], //替换你的数据
      };
      const pointGroups = signatureData.data;
      this.$refs.signaturePad.fromData(pointGroups);
    },
  },
};
</script>
<style scoped>
.box {
  width: 1000px;
  height: 500px;
  border: 1px solid red;
}
</style>

四、vue-esign 和 vue-signature-pad 的区别?

4.1 共同点

  • 兼容 PC 和 移动端;
  • 同时支持 vue2vue3;
  • 画布自适应屏幕大小变化(窗口缩放、屏幕旋转时画布无需重置,自动校正坐标);
  • 自定义画布尺寸,画笔粗细、颜色,画布背景色。

4.2 不同点

  • vue-signature-pad 带笔压功能,可以根据笔的速度和方向来调整线条的粗细,不是固定粗细的,vue-esign 不支持此功能;
  • vue-signature-padvue2vue3 依赖安装版本不同;vue-esign 都版本向下兼容,不需要区分 vue2vue3;
  • vue-signature-pad 支持单步撤回,vue-esign 不支持此功能。

笔压对比效果

左边是 vue-signature-pad 插件,可以看到画笔线条不是固定粗细的,右边是 vue-esign 插件,线条是固定的粗细。
在这里插入图片描述


相关推荐

⭐ 浅谈 JSON 对象和 FormData 相互转换,打通前端与后端的通信血脉

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

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

相关文章

GitLab16.8配置webhooks、Jenkins2.4配置GitLab插件实现持续集成、配置宝塔面板实现持续部署(其三)

看本篇文章的前提是已经部署完GItlab和Jenkins服务器&#xff0c;已经可以手动构建成功&#xff0c;并且经过了很多次实践&#xff0c;对这两款软件基本熟悉。 建议大家按以下顺序看 前端自动化&#xff08;其一&#xff09;部署gitlab 前端自动化&#xff08;其二&#xff0…

第五篇:express路由路径方式(字符串,字符串模式,和正则)

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4d8; 引言&#xff1a; &#x1f4…

MYSQL基本查询(CURD:创建、读取、更新、删除)

文章目录 前言一、Create1.全列插入2.指定列插入3.插入否则更新4.替换 二、Retrieve1.SELECT列2.WHERE条件3.结果排序4.筛选分页结果 三、Update四、Delete1.删除数据2.截断表 五、插入查询结果六、聚合函数 前言 操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型…

用友U9 PatchFile.asmx 任意文件上传漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

CC攻击类型有几种?有什么有用的防御方法?

CC攻击&#xff08;Challenge Collapsar Attack&#xff09;是一种复杂的网络攻击&#xff0c;它利用了一种分布式拒绝服务攻击工具来发动攻击。这种攻击方式通常由黑客组织或恶意竞争者发起&#xff0c;旨在破坏目标网络或系统的正常运行。 CC攻击的核心在于使用大量的虚假请…

如何选择便捷安全的黄金交易平台?

黄金交易平台的介绍 黄金交易平台是一个提供方便、安全的方式进行黄金交易的网上平台。 投资者可以通过这些平台进行黄金的买卖&#xff0c;参与黄金市场的投资活动。 这些平台提供了一个简单易用的界面&#xff0c;让投资者可以方便地进行交易操作。 选择合适的黄金交易平台…

Kafka-服务端-GroupMetadataManager

GroupMetadataManager是GroupCoordinator中负责管理Consumer Group元数据以及其对应offset信息的组件。 GroupMetadataManager底层使用Offsets Topic,以消息的形式存储Consumer Group的GroupMetadata信息以及其消费的每个分区的offset,如图所示。 consumer_offsets的某Partiti…

LLM之Agent(九)| 通过API集成赋能Autogen Multi-Agent系统

随着大型语言模型的快速发展&#xff0c;构建基于LLM驱动的自治代理&#xff08;autonomous agents&#xff09;已经成为一个备受关注的话题。仅在过去一年中&#xff0c;就出现了许多基于这一理念的新技术和框架。 ​ 本文将探索微软开源的Agent框架&#xff1a;Autogen…

259:vue+openlayers: 显示海量多边形数据,10ms加载完成

第259个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers项目中通过WebGLVectorLayerRenderer方式加载海量多边形数据。这里相当于将海量的数据放在同一个层的source中,然后通过webglTile的方式渲染出这一层。 本示例数据为5000个多边形,加载速度超级快。 直接…

D35XB80-ASEMI整流桥D35XB80参数、封装、尺寸

编辑&#xff1a;ll D35XB80-ASEMI整流桥D35XB80参数、封装、尺寸 型号&#xff1a;D35XB80 品牌&#xff1a;ASEMI 封装&#xff1a;GBJ-5 最大重复峰值反向电压&#xff1a;800V 最大正向平均整流电流(Vdss)&#xff1a;35A 功率(Pd)&#xff1a; 芯片个数&#xff1…

Web 开发 6:Redis 缓存(Flask项目使用Redis并同时部署到Docker详细流程 附项目源码)

大家好&#xff01;欢迎来到第六篇 Web 开发教程&#xff0c;今天我们将探讨一个非常重要的话题&#xff1a;Redis 缓存。作为一个互联网开发者&#xff0c;你一定知道在处理大量请求时&#xff0c;性能优化是至关重要的。而 Redis 缓存正是帮助我们提升系统性能的利器。Redis …

tcpdump 抓包无法落盘

文章目录 问题背景解决办法 问题背景 在嵌入式设备中(Linux系统)&#xff0c;为了分析两个网络节点的通讯问题&#xff0c;往往需要用到tcpdump&#xff0c;抓一个.pcap的包在PC端进行分析。博主在实际操作中发现&#xff0c;抓包无法实时落盘。 解决办法 # 下面的命令是写在…

MATLAB环境下一种贝叶斯稀疏盲反卷积算法

稀疏盲反卷积贝叶斯估计方法通常使用伯努利-高斯分布(BG)先验的稀疏序列建模&#xff0c;并利用马尔可夫链蒙特卡罗(MCMC)方法进行未知估计。然而&#xff0c;BG模型的离散性会有计算瓶颈。为了解决这个问题&#xff0c;提出一个替代方案&#xff0c;采用MCMC方法对稀疏序列进行…

Mac下查看、配置和使用环境变量

Mac下查看、配置和使用环境变量 一&#xff1a;Mac怎么查看环境变量命令 printenv一&#xff1a;这个命令会一次性列出所有环境变量的键值对&#xff0c;输出格式为&#xff1a; VAR1value1 VAR2value2 ...二&#xff1a; 也可以通过给这个命令加上环境变量名参数&#xff0…

Linux CPU 负载说明

一、背景 工作中我们经常遇到CPU 负载高&#xff0c;CPU负载高意味着什么&#xff1f; CPU的负载是怎么计算的&#xff1f; top指令中的各个指标代表什么含义&#xff1f; 二、CPU 负载计算方法 在系统出现负载问题&#xff0c;通常会使用uptime和top确认负载&#xff0c;这两…

云微呼云呼叫中心:颠覆传统客户服务模式的数字化创新

1. 引言 随着云计算技术的不断成熟和普及&#xff0c;云呼叫中心作为一种全新的客户服务解决方案&#xff0c;正在以其高效、灵活和智能的特点&#xff0c;逐渐颠覆传统的客户服务模式。本文将深入探讨云呼叫中心的定义、特点、优势以及对企业的重要意义。 2. 云呼叫中心的定…

龙行龘龘•前程朤朤 | 联诚发2023年度述职大会与年会圆满举行

1月27日上午&#xff0c;以“不忘初心・砥砺前行”为主题的2023年度述职大会在联诚发深圳总部隆重举行&#xff0c;本次大会总结了过去一年的成就与不足&#xff0c;展望了未来一年的方向和目标。联诚发创始人兼总裁龙平芳、董秘毛强军、各部门负责人以及深圳总部全体职工出席了…

JSP概述和基本使用

1&#xff0c;JSP 概述 JSP&#xff08;全称&#xff1a;Java Server Pages&#xff09;&#xff1a;Java 服务端页面。是一种动态的网页技术&#xff0c;其中既可以定义 HTML、JS、CSS等静态内容&#xff0c;还可以定义 Java代码的动态内容&#xff0c;也就是 JSP HTML Jav…

LabVIEW探测器CAN总线系统

介绍了一个基于FPGA和LabVIEW的CAN总线通信系统&#xff0c;该系统专为与各单机进行系统联调测试而设计。通过设计FPGA的CAN总线功能模块和USB功能模块&#xff0c;以及利用LabVIEW开发的上位机程序&#xff0c;系统成功实现了CAN总线信息的收发、存储、解析及显示功能。测试结…

【更新】省级产业结构合理化+高度化+高级化数据(2000-2022年)

数据指标测算&#xff1a; &#xff08;1&#xff09;参考袁航等人&#xff08;2018&#xff09;的测算方法&#xff0c;采用泰尔指数来衡量产业结构合理化指标&#xff0c;用产业结构层次系数来表示产业结构高度化指标&#xff1a; &#xff08;2&#xff09;参考张欣艳等人&…