前言:
这段时间项目迭代时遇到了一个新需求,基于react实现一个Pc版电子签名功能,并生成图片上传。于是我想到了signature_pad,并且在项目使用了这个插件
不得不说,用别人造的轮子是真的香,出于好奇,想用原生实现一下电子签名的功能
以下是实现过程
HTML和css可以参照源码,这里不过多介绍
首先引入eventBus,方便代码解耦
然后实现Base基类存放公共方法和属性,后续有啥共用属性或方法可以往这加
//基类:公共方法和属性
import event from './eventBus.js'
export default class Base {
constructor(canvasEle, dom = document) {
this.event = event //注册发布订阅
this.canvasEle = canvasEle //待操作的画布标签
this.dom = dom //dom
return this;
}
}
完成之后,我们先实现Pc版的电子签名功能,新建一个PcPrint继承自Base,参照之前写的鼠标拖拽案例,实现在canvas上拖拽功能,并将事件结果的坐标发布出去。
其中clearDefaultEvent函数和getClient函数在Base类中实现
// PC端,鼠标事件
import Base from './base.js'
let that = null
export default class PcPrint extends Base {
constructor(ele, dom) {
super(ele, dom)
that = this //注册全局this
this.init()
return this;
}
init() {
that.canvasEle.addEventListener('mousedown', that.onMouseDown)
}
onMouseDown(e = event) {
that.clearDefaultEvent(e)
that.dom.addEventListener('mouseup', that.onMouseUp) //给dom添加mouseup避免产生鼠标点下时,移出画布造成其他的问题
that.canvasEle.addEventListener('mousemove', that.onMouseMove)
that.event.emitEvent('pointStart', that.getClient(e)) //触发开始签字事件
}
onMouseUp(e = event) {
that.clearDefaultEvent(e)
that.event.emitEvent('pointEnd') //触发结束签字事件
that.canvasEle.removeEventListener('mousemove', that.onMouseMove) //移除移动事件
}
onMouseMove(e = event) {
that.clearDefaultEvent(e)
that.event.emitEvent('pointMove', that.getClient(e)) //触发签字事件
}
}
Base类添加以下代码:
/**
* 取消默认事件和事件冒泡
* @param e 事件对象
*/
clearDefaultEvent(e) {
e.preventDefault()
e.stopPropagation()
}
/**
* 获取事件元素离body可视区域的坐标
* @param target 事件目标
*/
getClient(target) {
return {
x: target.clientX,
y: target.clientY
}
}
接着,我们对事件抛出的三个发布进行订阅,新建Print类,对获取的坐标通过canvas进行绘制
import Base from "./Base.js"
import PcPrint from './pc.js';
// import MobilePrint from './mobile.js';
let that = null
export default class Print extends Base {
constructor(canvasEle, options, dom) {
super(canvasEle, dom)
that = this
this.options = options //配置画笔颜色,粗细,是否开启移动端或PC端,
this.init() //初始化属性,配置,注册发布订阅等
this.initCanvas() //初始化画布
return this
}
init() {
//Pc和Mobile启用开关
this.Pc = this.options.Pc ? (new PcPrint(this.canvasEle)) : null
// this.Mobile = this.options.Mobile ? (new MobilePrint(this.canvasEle)) : null
this.point = null //存储上一次坐标
this.event.onEvent('pointMove', that.pointMove) //订阅签字事件
this.event.onEvent('pointStart', that.pointStart) //订阅签字开始事件
this.event.onEvent('pointEnd', that.pointEnd) //订阅签字结束事件
}
initCanvas() {
this.clientRect = this.canvasEle.getBoundingClientRect() // 获取标签相对可视区域的偏移量
this.canvasEle.width = this.canvasEle.parentNode.offsetWidth //设置为父元素的宽
this.canvasEle.height = this.canvasEle.parentNode.offsetHeight //设置为父元素的高
this.context = this.canvasEle.getContext('2d')
this.context.strokeStyle = this.options.color; // 线条颜色
this.context.lineWidth = this.options.weight; // 线条宽度
}
pointStart(point) {
that.point = that.shiftingPosition(point, that.clientRect) //初始化起始位置
}
pointEnd() {
that.point = null //清空起始位置
}
pointMove(point) {
that.canvasDraw(that.shiftingPosition(point, that.clientRect)) //签字效果
}
canvasDraw(point) { //画布操作
this.context.beginPath() //新建(重置)路径
this.context.moveTo(this.point.x, this.point.y) //画布绘画起始点移动到前一个坐标
this.context.lineTo(point.x, point.y) //画布从前一个坐标到当前坐标
this.context.stroke() //从moveTo到lineTo进行绘制
this.context.closePath() //创建从当前坐标回到前一个坐标的路径
that.point = point //将此次坐标赋值给下一次移动时的前一个坐标
}
}
考虑到canvas的偏移问题,在Base中添加shiftingPosition函数,解决画布绘制时坐标偏移问题
/**
* 抵消画布偏移
* @param point 当前坐标
* @param shift 偏移量
*/
shiftingPosition(point, shift) {
return {
x: point.x - shift.left,
y: point.y - shift.top
}
}
最后,在index中实例化电子签名
Pc端实现完成之后是Mobile端,代码大同小异,除了事件类型不用之外,还一点就是移动端的多指触碰支持,touchevent支持双指事件,此时我们要判断是否单指输入
// Mobile端,触摸事件
import Base from './base.js'
let that = null
export default class MobilePrint extends Base {
constructor(ele, dom) {
super(ele, dom)
that = this //注册全局this
this.init()
return this;
}
init() {
that.canvasEle.addEventListener('touchstart', that.onTouchStart)
}
onTouchStart(e = event) {
that.clearDefaultEvent(e)
that.canvasEle.addEventListener('touchend', that.onTouchEnd) //没有像pc一样给dom添加touchend,因为touchmove是基于touchstart和touchend之间触发的,只要touchend触发,touchmove便失效
that.canvasEle.addEventListener('touchmove', that.onTouchMove)
that.event.emitEvent('pointStart', that.getClient(e.touches[0])) //这里可以做一个判断e.touches是否只有一个(e.touches表示有几个手指触碰)
}
onTouchEnd(e = event) {
that.clearDefaultEvent(e)
that.event.emitEvent('pointEnd')
that.canvasEle.removeEventListener('touchmove', that.onTouchMove)
}
onTouchMove(e = event) {
that.clearDefaultEvent(e)
that.event.emitEvent('pointMove', that.getClient(e.touches[0]))
}
}
在移动端实现的效果:
最后:
附上源码地址:Gitee
感谢你的阅读,如果这篇文章对你有帮助,希望三连支持一下,你的支持是我创作的动力,同时也欢迎大佬建议指正