您好,登录后才能下订单哦!
在现代Web应用中,电子签名功能变得越来越常见。无论是合同签署、表单提交还是其他需要用户确认的场景,电子签名都提供了一种便捷的方式来获取用户的确认。本文将详细介绍如何使用Vue.js和HTML5的Canvas技术来实现一个简单的电子签名功能。
Vue.js 是一个用于构建用户界面的渐进式JavaScript框架。它的核心库专注于视图层,易于与其他库或现有项目集成。Vue.js 的设计目标是通过尽可能简单的API实现响应的数据绑定和组合的视图组件。
HTML5 的 <canvas>
元素提供了一个用于绘制图形的API。通过JavaScript,我们可以在Canvas上绘制各种图形、文本、图像等。Canvas 非常适合用于实现绘图、动画、游戏等需要动态图形渲染的场景。
在开始之前,我们需要创建一个新的Vue项目。如果你还没有安装Vue CLI,可以通过以下命令进行安装:
npm install -g @vue/cli
然后,创建一个新的Vue项目:
vue create vue-canvas-signature
在项目创建过程中,你可以选择默认配置或手动选择需要的特性。创建完成后,进入项目目录并启动开发服务器:
cd vue-canvas-signature
npm run serve
在 src/components
目录下创建一个新的组件文件 SignaturePad.vue
。这个组件将包含我们的电子签名功能。
<template>
<div class="signature-pad">
<canvas ref="canvas" @mousedown="startDrawing" @mousemove="draw" @mouseup="stopDrawing" @mouseleave="stopDrawing"></canvas>
<div class="controls">
<button @click="clearCanvas">清除</button>
<button @click="saveSignature">保存</button>
</div>
</div>
</template>
<script>
export default {
name: 'SignaturePad',
data() {
return {
isDrawing: false,
lastX: 0,
lastY: 0,
};
},
methods: {
startDrawing(event) {
this.isDrawing = true;
const canvas = this.$refs.canvas;
const rect = canvas.getBoundingClientRect();
this.lastX = event.clientX - rect.left;
this.lastY = event.clientY - rect.top;
},
draw(event) {
if (!this.isDrawing) return;
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
ctx.beginPath();
ctx.moveTo(this.lastX, this.lastY);
ctx.lineTo(x, y);
ctx.stroke();
this.lastX = x;
this.lastY = y;
},
stopDrawing() {
this.isDrawing = false;
},
clearCanvas() {
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
},
saveSignature() {
const canvas = this.$refs.canvas;
const image = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = image;
link.download = 'signature.png';
link.click();
},
},
mounted() {
const canvas = this.$refs.canvas;
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
const ctx = canvas.getContext('2d');
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.lineCap = 'round';
},
};
</script>
<style scoped>
.signature-pad {
display: flex;
flex-direction: column;
align-items: center;
}
canvas {
border: 1px solid #000;
background-color: #fff;
width: 100%;
height: 300px;
}
.controls {
margin-top: 10px;
}
button {
margin: 0 5px;
padding: 5px 10px;
cursor: pointer;
}
</style>
在 mounted
钩子中,我们初始化了Canvas的宽度和高度,并设置了绘图上下文的一些默认属性,如线条颜色、宽度和线条端点的样式。
mounted() {
const canvas = this.$refs.canvas;
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
const ctx = canvas.getContext('2d');
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.lineCap = 'round';
}
我们监听了Canvas的 mousedown
、mousemove
、mouseup
和 mouseleave
事件,以便在用户开始绘制、移动鼠标、停止绘制或离开Canvas时执行相应的操作。
startDrawing(event) {
this.isDrawing = true;
const canvas = this.$refs.canvas;
const rect = canvas.getBoundingClientRect();
this.lastX = event.clientX - rect.left;
this.lastY = event.clientY - rect.top;
},
draw(event) {
if (!this.isDrawing) return;
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
ctx.beginPath();
ctx.moveTo(this.lastX, this.lastY);
ctx.lineTo(x, y);
ctx.stroke();
this.lastX = x;
this.lastY = y;
},
stopDrawing() {
this.isDrawing = false;
},
在 draw
方法中,我们使用Canvas的绘图API来绘制线条。通过 beginPath
、moveTo
和 lineTo
方法,我们可以在Canvas上绘制出用户鼠标移动的路径。
clearCanvas
方法使用 clearRect
方法清除Canvas上的所有内容,从而实现清除签名的功能。
clearCanvas() {
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
},
saveSignature
方法将Canvas上的内容转换为图像,并通过创建一个 <a>
元素来触发下载。
saveSignature() {
const canvas = this.$refs.canvas;
const image = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = image;
link.download = 'signature.png';
link.click();
},
为了支持触摸设备,我们需要添加对 touchstart
、touchmove
和 touchend
事件的监听。
<template>
<div class="signature-pad">
<canvas ref="canvas"
@mousedown="startDrawing"
@mousemove="draw"
@mouseup="stopDrawing"
@mouseleave="stopDrawing"
@touchstart="startDrawingTouch"
@touchmove="drawTouch"
@touchend="stopDrawing"></canvas>
<div class="controls">
<button @click="clearCanvas">清除</button>
<button @click="saveSignature">保存</button>
</div>
</div>
</template>
<script>
export default {
name: 'SignaturePad',
data() {
return {
isDrawing: false,
lastX: 0,
lastY: 0,
};
},
methods: {
startDrawing(event) {
this.isDrawing = true;
const canvas = this.$refs.canvas;
const rect = canvas.getBoundingClientRect();
this.lastX = event.clientX - rect.left;
this.lastY = event.clientY - rect.top;
},
startDrawingTouch(event) {
this.isDrawing = true;
const canvas = this.$refs.canvas;
const rect = canvas.getBoundingClientRect();
const touch = event.touches[0];
this.lastX = touch.clientX - rect.left;
this.lastY = touch.clientY - rect.top;
},
draw(event) {
if (!this.isDrawing) return;
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
ctx.beginPath();
ctx.moveTo(this.lastX, this.lastY);
ctx.lineTo(x, y);
ctx.stroke();
this.lastX = x;
this.lastY = y;
},
drawTouch(event) {
if (!this.isDrawing) return;
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
const touch = event.touches[0];
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
ctx.beginPath();
ctx.moveTo(this.lastX, this.lastY);
ctx.lineTo(x, y);
ctx.stroke();
this.lastX = x;
this.lastY = y;
},
stopDrawing() {
this.isDrawing = false;
},
clearCanvas() {
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
},
saveSignature() {
const canvas = this.$refs.canvas;
const image = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = image;
link.download = 'signature.png';
link.click();
},
},
mounted() {
const canvas = this.$refs.canvas;
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
const ctx = canvas.getContext('2d');
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.lineCap = 'round';
},
};
</script>
为了实现撤销功能,我们可以使用一个数组来存储每一步的绘图操作。每次绘制时,我们将当前Canvas的状态保存到数组中。当用户点击撤销按钮时,我们可以从数组中恢复上一个状态。
data() {
return {
isDrawing: false,
lastX: 0,
lastY: 0,
history: [],
};
},
methods: {
startDrawing(event) {
this.isDrawing = true;
const canvas = this.$refs.canvas;
const rect = canvas.getBoundingClientRect();
this.lastX = event.clientX - rect.left;
this.lastY = event.clientY - rect.top;
this.saveState();
},
draw(event) {
if (!this.isDrawing) return;
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
ctx.beginPath();
ctx.moveTo(this.lastX, this.lastY);
ctx.lineTo(x, y);
ctx.stroke();
this.lastX = x;
this.lastY = y;
},
stopDrawing() {
this.isDrawing = false;
this.saveState();
},
saveState() {
const canvas = this.$refs.canvas;
const image = canvas.toDataURL();
this.history.push(image);
},
undo() {
if (this.history.length > 1) {
this.history.pop();
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
const image = new Image();
image.src = this.history[this.history.length - 1];
image.onload = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(image, 0, 0);
};
}
},
clearCanvas() {
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
this.history = [];
this.saveState();
},
saveSignature() {
const canvas = this.$refs.canvas;
const image = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = image;
link.download = 'signature.png';
link.click();
},
},
重做功能与撤销功能类似,我们可以使用另一个数组来存储被撤销的操作。当用户点击重做按钮时,我们可以从该数组中恢复被撤销的操作。
data() {
return {
isDrawing: false,
lastX: 0,
lastY: 0,
history: [],
redoHistory: [],
};
},
methods: {
startDrawing(event) {
this.isDrawing = true;
const canvas = this.$refs.canvas;
const rect = canvas.getBoundingClientRect();
this.lastX = event.clientX - rect.left;
this.lastY = event.clientY - rect.top;
this.saveState();
},
draw(event) {
if (!this.isDrawing) return;
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
ctx.beginPath();
ctx.moveTo(this.lastX, this.lastY);
ctx.lineTo(x, y);
ctx.stroke();
this.lastX = x;
this.lastY = y;
},
stopDrawing() {
this.isDrawing = false;
this.saveState();
},
saveState() {
const canvas = this.$refs.canvas;
const image = canvas.toDataURL();
this.history.push(image);
this.redoHistory = [];
},
undo() {
if (this.history.length > 1) {
const lastState = this.history.pop();
this.redoHistory.push(lastState);
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
const image = new Image();
image.src = this.history[this.history.length - 1];
image.onload = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(image, 0, 0);
};
}
},
redo() {
if (this.redoHistory.length > 0) {
const nextState = this.redoHistory.pop();
this.history.push(nextState);
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
const image = new Image();
image.src = nextState;
image.onload = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(image, 0, 0);
};
}
},
clearCanvas() {
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
this.history = [];
this.redoHistory = [];
this.saveState();
},
saveSignature() {
const canvas = this.$refs.canvas;
const image = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = image;
link.download = 'signature.png';
link.click();
},
},
在某些场景下,我们可能需要对用户的签名进行验证,以确保签名的有效性。我们可以通过检查Canvas上是否有绘制内容来实现简单的签名验证。
methods: {
isSignatureValid() {
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
if (data[i + 3] !== 0) {
return true;
}
}
return false;
},
saveSignature() {
if (!this.isSignatureValid()) {
alert('请先签名');
return;
}
const canvas = this.$refs.canvas;
const image = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = image;
link.download = 'signature.png';
link.click();
},
},
通过本文的介绍,我们学习了如何使用Vue.js和HTML5的Canvas技术来实现一个简单的电子签名功能。我们实现了基本的绘制、清除、保存功能,并进一步优化
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。