react实现电子签名
效果图
在项目中安装 react-signature-canvas
命令:
npm install react-signature-canvas --save
react电子签名组件封装
import { Button } from 'antd';
import React, { useRef } from 'react';
import SignatureCanvas from 'react-signature-canvas';
const ElectronicSignature = () => {
const signatureRef = useRef();
// 处理保存签名的方法
const handleSaveSignature = () => {
const signatureData = signatureRef.current.toDataURL(); // 将签名保存为DataURL
console.log(signatureData, '999999999'); // 这里可以将签名数据传送到后端保存或处理
};
// 清除签名
const handleClearSignature = () => {
signatureRef.current.clear();
};
return (
<div>
<h2>电子签名</h2>
<div>
<SignatureCanvas
ref={signatureRef}
penColor="black" // 可以设置笔的颜色
canvasProps={{ width: 500, height: 200, className: 'signature-canvas' }} // 设置Canvas的尺寸和样式
/>
</div>
<div>
<Button onClick={handleSaveSignature}>保存签名</Button>
<Button onClick={handleClearSignature}>清除签名</Button>
</div>
</div>
);
};
export default ElectronicSignature;
电子签名(原生版)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<canvas></canvas>
<div>
<button onclick="cancel()">取消</button>
<button onclick="save()">保存</button>
</div>
</body>
<script>
// 配置内容
const config = {
width: 400, // 宽度
height: 200, // 高度
lineWidth: 5, // 线宽
strokeStyle: 'red', // 线条颜色
lineCap: 'round', // 设置线条两端圆角
lineJoin: 'round', // 线条交汇处圆角
};
// 获取canvas 实例
const canvas = document.querySelector('canvas');
// 设置宽高
canvas.width = config.width;
canvas.height = config.height;
// 设置一个边框
canvas.style.border = '1px solid #000';
// 创建上下文
const ctx = canvas.getContext('2d');
// 设置填充背景色
ctx.fillStyle = 'transparent';
// 绘制填充矩形
ctx.fillRect(
0, // x 轴起始绘制位置
0, // y 轴起始绘制位置
config.width, // 宽度
config.height, // 高度
);
// 保存上次绘制的 坐标及偏移量
const client = {
offsetX: 0, // 偏移量
offsetY: 0,
endX: 0, // 坐标
endY: 0,
};
// 判断是否为移动端
const mobileStatus = /Mobile|Android|iPhone/i.test(navigator.userAgent);
// 初始化
const init = (event) => {
// 获取偏移量及坐标
const { offsetX, offsetY, pageX, pageY } = mobileStatus ? event.changedTouches[0] : event;
// 修改上次的偏移量及坐标
client.offsetX = offsetX;
client.offsetY = offsetY;
client.endX = pageX;
client.endY = pageY;
// 清除以上一次 beginPath 之后的所有路径,进行绘制
ctx.beginPath();
// 根据配置文件设置相应配置
ctx.lineWidth = config.lineWidth;
ctx.strokeStyle = config.strokeStyle;
ctx.lineCap = config.lineCap;
ctx.lineJoin = config.lineJoin;
// 设置画线起始点位
ctx.moveTo(client.endX, client.endY);
// 监听 鼠标移动或手势移动
window.addEventListener(mobileStatus ? 'touchmove' : 'mousemove', draw);
};
// 绘制
const draw = (event) => {
// 获取当前坐标点位
const { pageX, pageY } = mobileStatus ? event.changedTouches[0] : event;
// 修改最后一次绘制的坐标点
client.endX = pageX;
client.endY = pageY;
// 根据坐标点位移动添加线条
ctx.lineTo(pageX, pageY);
// 绘制
ctx.stroke();
};
// 结束绘制
const cloaseDraw = () => {
// 结束绘制
ctx.closePath();
// 移除鼠标移动或手势移动监听器
window.removeEventListener('mousemove', draw);
};
// 创建鼠标/手势按下监听器
window.addEventListener(mobileStatus ? 'touchstart' : 'mousedown', init);
// 创建鼠标/手势 弹起/离开 监听器
window.addEventListener(mobileStatus ? 'touchend' : 'mouseup', cloaseDraw);
// 取消-清空画布
const cancel = () => {
// 清空当前画布上的所有绘制内容
ctx.clearRect(0, 0, config.width, config.height);
};
// 保存-将画布内容保存为图片
const save = () => {
// 将canvas上的内容转成blob流
canvas.toBlob((blob) => {
// 获取当前时间并转成字符串,用来当做文件名
const date = Date.now().toString();
// 创建一个 a 标签
const a = document.createElement('a');
// 设置 a 标签的下载文件名
a.download = `${date}.png`;
// 设置 a 标签的跳转路径为 文件流地址
a.href = URL.createObjectURL(blob);
// 手动触发 a 标签的点击事件
a.click();
// 移除 a 标签
a.remove();
});
};
</script>
</html>
以上代码实现步骤:
1、在body中添加canvas标签
在在body中添加canvas标签,添加两个按钮,取消和保存。
<body>
<canvas></canvas>
<div>
<button>取消</button>
<button>保存</button>
</div>
</body>
2、添加文件
这里全程使用js进行样式设置及添加。
// 配置内容
const config = {
width: 400, // 宽度
height: 200, // 高度
lineWidth: 5, // 线宽
strokeStyle: 'red', // 线条颜色
lineCap: 'round', // 设置线条两端圆角
lineJoin: 'round', // 线条交汇处圆角
}
3、获取canvas实例
使用querySelector获取canvas的dom实例,并设置样式和创建上下文。
// 获取canvas 实例
const canvas = document.querySelector('canvas')
// 设置宽高
canvas.width = config.width
canvas.height = config.height
// 设置一个边框,方便我们查看及使用
canvas.style.border = '1px solid #000'
// 创建上下文
const ctx = canvas.getContext('2d')
4、基础设置
将canvas的填充色为透明,绘制填充一个矩形,作为画布,如果不设置这个填充背景色,在初始渲染的时候是一个黑色背景,即默认色。
// 设置填充背景色
ctx.fillStyle = 'transparent'
// 绘制填充矩形
ctx.fillRect(
0, // x 轴起始绘制位置
0, // y 轴起始绘制位置
config.width, // 宽度
config.height // 高度
);
5、上次绘制路径保存
这里需要声明一个对象,用来记录上一次绘制的路径结束坐标点及偏移量。
- 为啥需要保存偏移量呢,因为鼠标和画布上的距离是存在一定的偏移距离,在我们绘制的过程中需要减去这个偏移量,才是我们实际的绘制坐标。
- 但我发现chrome中不需要减去这个偏移量,拿到的就是实际的坐标,之前在微信小程序中使用就需要减去偏移量,需要在小程序中使用的朋友需要注意这一点。
// 保存上次绘制的 坐标及偏移量
const client = {
offsetX: 0, // 偏移量
offsetY: 0,
endX: 0, // 坐标
endY: 0
}
6、设备兼容
需要它不仅可以在web端使用,还需要在移动端使用,需要给它做设备兼容处理。通过调用navigator.userAgent获取当前设备信息,进行正则匹配判断。
// 判断是否为移动端
const mobileStatus = (/Mobile|Android|iPhone/i.test(navigator.userAgent))
7、初始化
在监听鼠标按下(mousedown)(web端)/触摸开始(touchstart)的时候进行初始化,事件监听采用addEventListener
// 创建鼠标/手势按下监听器
window.addEventListener(mobileStatus ? "touchstart" : "mousedown", init)
8、声明初始化方法
添加一个init方法作为监听鼠标按下/触摸开始的回调方法。
需要获取到当前鼠标按下/触摸开始的偏移量和坐标,进行起始点绘制。
web端可以直接通过event中取到,而移动端则需要在event.changedTouches[0]中取到。
// 初始化
const init = event => {
// 获取偏移量及坐标
const { offsetX, offsetY, pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
// 修改上次的偏移量及坐标
client.offsetX = offsetX
client.offsetY = offsetY
client.endX = pageX
client.endY = pageY
// 清除以上一次 beginPath 之后的所有路径,进行绘制
ctx.beginPath()
// 根据配置文件设置进行相应配置
ctx.lineWidth = config.lineWidth
ctx.strokeStyle = config.strokeStyle
ctx.lineCap = config.lineCap
ctx.lineJoin = config.lineJoin
// 设置画线起始点位
ctx.moveTo(client.endX, client.endY)
// 监听 鼠标移动或手势移动
window.addEventListener(mobileStatus ? "touchmove" : "mousemove", draw)
}
9、绘制
添加绘制draw方法,作为监听鼠标移动/触摸移动的回调方法
// 绘制
const draw = event => {
// 获取当前坐标点位
const { pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
// 修改最后一次绘制的坐标点
client.endX = pageX
client.endY = pageY
// 根据坐标点位移动添加线条
ctx.lineTo(pageX , pageY )
// 绘制
ctx.stroke()
}
10、结束绘制
添加了监听鼠标移动/触摸移动一定要记得取消监听并结束绘制,不然的话它会一直监听并绘制的。
这里创建一个cloaseDraw方法作为鼠标弹起/结束触摸的回调方法来结束绘制并移除鼠标移动/触摸移动的监听。
canvas结束绘制则需要调用closePath()让其结束绘制
// 结束绘制
const cloaseDraw = () => {
// 结束绘制
ctx.closePath()
// 移除鼠标移动或手势移动监听器
window.removeEventListener("mousemove", draw)
}
添加结束回调监听器
// 创建鼠标/手势 弹起/离开 监听器
window.addEventListener(mobileStatus ? "touchend" :"mouseup", cloaseDraw)
11、取消功能/清空画布
创建一个cancel的方法作为取消并清空画布使用
// 取消-清空画布
const cancel = () => {
// 清空当前画布上的所有绘制内容
ctx.clearRect(0, 0, config.width, config.height)
}
绑定
<button onclick="cancel()">取消</button>
12、保存功能
创建一个save的方法作为保存画布上的内容使用。
将画布上的内容保存为图片/文件的方法有很多,比较常见的是blob和toDataURL这两种方案,但toDataURL这哥们没blob强,适配也不咋滴。所以我们这里采用a标签 ➕ blob方案实现图片的保存下载。
// 保存-将画布内容保存为图片
const save = () => {
// 将canvas上的内容转成blob流
canvas.toBlob(blob => {
// 获取当前时间并转成字符串,用来当做文件名
const date = Date.now().toString()
// 创建一个 a 标签
const a = document.createElement('a')
// 设置 a 标签的下载文件名
a.download = `${date}.png`
// 设置 a 标签的跳转路径为 文件流地址
a.href = URL.createObjectURL(blob)
// 手动触发 a 标签的点击事件
a.click()
// 移除 a 标签
a.remove()
})
}
绑定
<button onclick="save()">保存</button>