前言
在现代数字化时代,电子签名成为了一种方便、高效且安全的签署文件的方式。本文将介绍电子签名的原理和实现方法,帮助你快速掌握这一重要的工具。
电子签名是什么?
电子签名是一种数字化的签名方式,用于验证和确认电子文档、合同或其他电子信息的真实性和完整性。它是传统纸质签名的数字化替代品,具有更高的效率、便捷性和安全性。大家看下面这个例子就会一目了然了。
一、手动实现一个简单的电子签名
下面的示例中,我们使用了 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 常用的属性
属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
width | Number | 800 | 画布宽度,即导出图片的宽度 |
height | Number | 300 | 画布高度,即导出图片的高度 |
lineWidth | Number | 4 | 画笔粗细 |
lineColor | String | #000000 | 画笔颜色 |
bgColor | String | 空 | 画布背景色,为空时画布背景透明,支持多种格式 ‘#ccc’,‘#E5A1A1’,‘rgb(229, 161, 161)’,‘rgba(0,0,0,.6)’,‘red’ |
isCrop | Boolean | false | 是否裁剪,在画布设定尺寸基础上裁掉四周空白部分 |
isClearBgColor | Boolean | true | 清空画布时(reset)是否同时清空设置的背景色(bgColor) |
format | String | image/png | 生成图片格式 image/jpeg(jpg格式下生成的图片透明背景会变黑色请慎用或指定背景色)、 image/webp |
quality | Number | 1 | 生成图片质量;在指定图片格式为 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 常用的属性
属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
width | String | 100% | 画布宽度 |
height | String | 100% | 画布高度 |
options | Object | {} | 设置画笔的选项,包括线条颜色、宽度、透明度等 |
images | Array | [] | 设置背景图片。可以是 [‘1.png’, ‘2.png’] 或者 [{ src: ‘1.png’, x: 0, y: 0 }, { src: ‘2.png’, x: 0, y: 10 }] |
customStyle | Object | {} | 设置画布的自定义样式。可以是一个CSS样式对象 |
scaleToDevicePixelRatio | Boolean | true | 设置是否将画布缩放到设备像素比 |
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
和 移动端; - 同时支持
vue2
和vue3
; - 画布自适应屏幕大小变化(窗口缩放、屏幕旋转时画布无需重置,自动校正坐标);
- 自定义画布尺寸,画笔粗细、颜色,画布背景色。
4.2 不同点
vue-signature-pad
带笔压功能,可以根据笔的速度和方向来调整线条的粗细,不是固定粗细的,vue-esign
不支持此功能;vue-signature-pad
的vue2
和vue3
依赖安装版本不同;vue-esign
都版本向下兼容,不需要区分vue2
和vue3
;vue-signature-pad
支持单步撤回,vue-esign
不支持此功能。
笔压对比效果
左边是 vue-signature-pad
插件,可以看到画笔线条不是固定粗细的,右边是 vue-esign
插件,线条是固定的粗细。