canvas实现手动签名包含(原生js和vue实现)
如果想要实现手动在画布上书写或者画画,需要学会canvas的画路径,这里我就不介绍了。
现在我们先说明一下签名的功能
当鼠标按下之后,开始移动鼠标就可以根据鼠标移动画出一条路径,然后弹起鼠标之后,就停止绘画
从上面的使用过程中我们可以设计实现的思路:
- 需要监听鼠标的按下和弹起事件,记录鼠标的状态,并且鼠标弹起和按下改变鼠标的状态
- 鼠标需要监测鼠标在画布第一次按下的时候的坐标
- 弹起鼠标和鼠标移动到画布外在
canvas画布上就不能绘图,就将坐标重置 - 监听鼠标在画布上的移动,需要监听
canvas画布的鼠标移动事件,前提是鼠标为按下状态 - 除了第一次每次移动就将移动终点作为新起点,然后依次往后画出路径
- 需要判断画布的边界,当鼠标移动出去之后,如果鼠标还是按压状态,进入之后就要开始绘图
原生js实现
根据上述
第一步
首先需要一个全局变量来设置鼠标的按下和弹起状态mouseStatus,需要将鼠标进入画布的坐标startX,startY
获取画布信息
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box{
width: 600px;
border: 1px solid #000;
}
#canvas:active {
cursor: pointer;
}
.img-box{
background-color: skyblue;
width: 200px;
height: 200px;
}
.img-box img{
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div class="box">
<canvas id="canvas" width="600" height="400"></canvas>
</div>
<button class="btn">保存</button>
<button class="clear">清除</button>
<div class="img-box">
</div>
<script>
/** @type {HTMLCanvasElement}**/
const canvas=document.querySelector("#canvas")
const ctx=canvas.getContext("2d")
let mouseStatus=false//默认是弹起状态
let startX=-1
let startY=-1
document.addEventListener("mousedown",function (e) {
mouseStatus=true
})
document.addEventListener("mouseup",function () {
mouseStatus=false
})
</script>
</body>
</html>
第二步
记录鼠标状态为按下并且第一次进入canvas画布的坐标(startX,startY)
第一种情况:在canvas画布区域按下鼠标
canvas.addEventListener("mousedown",function(e){
startX=e.offsetX
startY=e.offsetY
})
第二种情况:鼠标按下之后从画布外边移动到画布内部
canvas.addEventListener("mouseenter",function(e){
if(mouseStatus){
startX=e.offsetX
startY=e.offsetY
}
})
第三步 重置鼠标坐标的情况 第一种情况:鼠标弹起
document.addEventListener("mouseup",function () {
mouseStatus=false
startX=-1
startY=-1
})
第二种情况:鼠标离开画布
canvas.addEventListener("mouseleave",function(){
startX=-1
startY=-1
})
第四-六步
监测鼠标在canvas画布上的移动事件,然后开始绘图
canvas.addEventListener("mousemove",function (e) {
// 鼠标还是按下状态
if (mouseStatus) {
if(startX>=0&&startY>=0){
startDraw(e.offsetX,e.offsetY)
}
}
})
function startDraw(x,y) {
ctx.beginPath()
ctx.moveTo(startX,startY)
ctx.lineCap="round"
ctx.lineWidth="8"
ctx.lineTo(x,y)
startX=x
startY=y
ctx.stroke()
ctx.closePath()
}
清除画布内容
使用canvas的clearReact方法将将画布内容置空
document.querySelector(".clear").addEventListener("click",clear)
function clear(){
let w=canvas.width
let h=canvas.height
ctx.closePath()
ctx.clearRect(0,0,w,h)
ctx.beginPath()
}
原生js实现的源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box{
width: 600px;
border: 1px solid #000;
}
#canvas:active {
cursor: pointer;
}
.img-box{
background-color: skyblue;
width: 200px;
height: 200px;
}
.img-box img{
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div class="box">
<canvas id="canvas" width="600" height="400"></canvas>
</div>
<button class="btn">保存</button>
<button class="clear">清除</button>
<div class="img-box">
</div>
<script>
/** @type {HTMLCanvasElement}**/
const canvas=document.querySelector("#canvas")
const ctx=canvas.getContext("2d")
let startX=-1
let startY=-1
let mouseStatus=false
document.addEventListener("mousedown",function (e) {
mouseStatus=true
})
document.addEventListener("mouseup",function () {
mouseStatus=false
startX=-1
startY=-1
})
canvas.addEventListener("mousedown",function(e){
startX=e.offsetX
startY=e.offsetY
})
canvas.addEventListener("mousemove",function (e) {
// 鼠标还是按下状态
if (mouseStatus) {
if(startX>=0&&startY>=0){
startDraw(e.offsetX,e.offsetY)
}
}
})
canvas.addEventListener("mouseleave",function(){
startX=-1
startY=-1
})
canvas.addEventListener("mouseenter",function(e){
if(mouseStatus){
startX=e.offsetX
startY=e.offsetY
}
})
function startDraw(x,y) {
ctx.beginPath()
ctx.moveTo(startX,startY)
ctx.lineCap="round"
ctx.lineWidth="8"
ctx.lineTo(x,y)
startX=x
startY=y
ctx.stroke()
}
// 这里主要是用于将画出的canvas图形转化为图片,然后展示在页面上
let image=new Image();
document.querySelector(".btn").addEventListener("click",function () {
image.src
url=canvas.toDataURL({ format: 'image/png',quality: 1,width: 600,height: 400 });
image.src=url
console.log("url==",url)
document.querySelector(".img-box").appendChild(image)
})
document.body.append(image)
// 清空画布
document.querySelector(".clear").addEventListener("click",clear)
function clear(){
let w=canvas.width
let h=canvas.height
ctx.closePath()
ctx.clearRect(0,0,w,h)
ctx.beginPath()
}
</script>
</body>
</html>
vue实现canvas画布手动签名
vue实现的话原理与上述方法一致只是写法不同,这个是我在实际开发中使用的主要在el-dialog弹窗中实现,源码如下
<template>
<el-dialog
title=""
:visible.sync="dialogVisible"
width="800px"
@close="handleClose"
append-to-body
center
>
<div class="tip">请将鼠标移到黑色方框内书写</div>
<div class="content">
<canvas
ref="signal"
class="canvas"
@mousedown="canvasMouseDown"
@mousemove="canvasMouseMove"
@mouseenter="canvasMouseEnter"
@mouseleave="canvasMouseLeave"
></canvas>
</div>
<span
slot="footer"
class="dialog-footer"
>
<el-button @click="clearSign">清 除</el-button>
<el-button
type="primary"
@click="createSignatrueImg"
>保 存</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
data() {
return {
dialogVisible: false, //弹窗显示
startAxis: {
//鼠标开始画图的开始坐标,也即是canvas的开始画图坐标
x: -1,
y: -1,
},
context: null, //画布上下文
mouseStatus: false, //鼠标是不是按下状态,是:true,否false
documentMouseDown: null, //鼠标按下事件,主要用于关闭弹窗的时候移除全局事件
documentMouseUp: null, //鼠标弹起事件
};
},
methods: {
/**
* 打开弹窗的方法,使用this.$refs.[弹窗组件ref].show()打开
* **/
show() {
this.dialogVisible=true
// 鼠标按下
this.documentMouseDown = () => {
this.mouseStatus = true;
};
document.addEventListener("mousedown", this.documentMouseDown);
// 鼠标抬起
this.documentMouseUp = () => {
this.mouseStatus = false;
this.startAxis.x = -1;
this.startAxis.y = -1;
};
document.addEventListener("mouseup", this.documentMouseUp);
// 这一步必须要,否则获取不到元素
this.$nextTick(() => {
const canvas = this.$refs.signal;
this.context = canvas.getContext("2d");
let content = document.querySelector(".content");
canvas.width = content.clientWidth;
canvas.height = content.clientHeight;
});
},
/**
* 生成签名图片
* **/
createSignatrueImg() {
// 截取图片的类型
let mime = "image/png";
const canvas = this.$refs.signal;
// 判断canvas内容区是否为空
let blank = document.createElement("canvas");
blank.width = canvas.width;
blank.height = canvas.height;
this.dialogVisible = false;
if (canvas.toDataURL() == blank.toDataURL()) {
return;
}
let base64 = canvas.toDataURL({
format: mime,
quality: 1,
width: 60,
height: 30,
});
// 先将base64转为blob,再转为file
let arr = base64.split(",");
// 使用 atob() 函数将 base64 字符串解码为二进制字符串
let bstr = atob(arr[1]);
let n = bstr.length;
// 使用 Uint8Array 构造函数将二进制字符串转换为字节数组
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
//将二进制字符串数组转为Blob
let blob = new Blob([u8arr], { type: mime });
// 将Blob转为file
let file = new File([blob], "signature.png");
this.$emit("update", file);
},
/**
* 清除画布
* **/
clearSign() {
const canvas = this.$refs.signal;
let w = canvas.width;
let h = canvas.height;
this.context.closePath();
this.context.clearRect(0, 0, w, h);
this.context.beginPath();
},
/**
* canvas的鼠标按下事件
* **/
canvasMouseDown(e) {
this.startAxis.x = e.offsetX;
this.startAxis.y = e.offsetY;
},
/**
* canvas的鼠标移动事件
* **/
canvasMouseMove(e) {
// 鼠标是按下状态
if (this.mouseStatus) {
if (this.startAxis.x >= 0 && this.startAxis.y >= 0) {
this.startDraw(e.offsetX, e.offsetY);
}
}
},
/**
* canvas画图写字
* **/
startDraw(x, y) {
this.context.beginPath();
this.context.moveTo(this.startAxis.x, this.startAxis.y);
this.context.lineCap = "round";
this.context.lineWidth = "5";
this.context.lineTo(x, y);
this.startAxis.x = x;
this.startAxis.y = y;
this.context.stroke();
this.context.closePath();
},
/**
* canvas的鼠标进入事件
* **/
canvasMouseEnter(e) {
if (this.mouseStatus) {
this.startAxis.x = e.offsetX;
this.startAxis.y = e.offsetY;
}
},
/**
* canvas的鼠标离开事件
* **/
canvasMouseLeave() {
this.startAxis.x = -1;
this.startAxis.y = -1;
},
/**
* 弹窗关闭事件,清除全局监听事件
* **/
handleClose() {
document.removeEventListener("mousedown", this.documentMouseDown);
document.removeEventListener("mouseup", this.documentMouseUp);
},
},
};
</script>
<style lang="scss" scoped>
.tip {
margin-bottom: 10px;
}
.content {
width: 600px;
height: 300px;
width: 100%;
border: 1px solid #000;
border-radius: 4px;
padding: 0;
.canvas:hover {
cursor: pointer;
}
}
</style>