JS案例:支持PC端和Mobile端的Canvas电子签名功能

简介: JS案例:支持PC端和Mobile端的Canvas电子签名功能

前言:

这段时间项目迭代时遇到了一个新需求,基于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中实例化电子签名

<script type="module">
    import Print from "./js/print.js"
    new Print(printBox,{
        Pc:true,
        Mobile:true,
        color:'lightcoral',
        weight:5
    })
</script>


效果如下:

1.gif


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]))
    }
}

在移动端实现的效果:

1.gif

最后:

附上源码地址:Gitee


相关文章
|
1月前
|
JavaScript 前端开发
JavaScript分页功能
JavaScript分页功能
|
15天前
|
JavaScript
js实现简洁实用的网页计算器功能源码
这是一款使用js实现简洁实用的网页计算器功能源码。可实现比较基本的加减乘除四则运算功能,界面简洁实用,是一款比较基本的js运算功能源码。该源码可兼容目前最新的各类主流浏览器。
23 2
|
1月前
|
人工智能 JavaScript 网络安全
ToB项目身份认证AD集成(三完):利用ldap.js实现与windows AD对接实现用户搜索、认证、密码修改等功能 - 以及针对中文转义问题的补丁方法
本文详细介绍了如何使用 `ldapjs` 库在 Node.js 中实现与 Windows AD 的交互,包括用户搜索、身份验证、密码修改和重置等功能。通过创建 `LdapService` 类,提供了与 AD 服务器通信的完整解决方案,同时解决了中文字段在 LDAP 操作中被转义的问题。
|
1月前
|
JavaScript 前端开发 API
|
1月前
|
JavaScript API UED
vue.js怎么实现全屏显示功能
【10月更文挑战第7天】
20 1
|
1月前
|
资源调度 JavaScript UED
如何使用Vue.js实现单页应用的路由功能
【10月更文挑战第1天】如何使用Vue.js实现单页应用的路由功能
|
1月前
|
JavaScript 搜索推荐
JS中的模糊查询功能
JS中的模糊查询功能
27 1
|
1月前
|
前端开发 JavaScript
使用 JavaScript 实现图片预览功能
使用 JavaScript 实现图片预览功能
27 0
|
1月前
|
JavaScript 安全 前端开发
js实现复制功能
js实现复制功能
18 0
|
1月前
|
前端开发 JavaScript
JavaScript动态渲染页面爬取——CSS位置偏移反爬案例分析与爬取实战
JavaScript动态渲染页面爬取——CSS位置偏移反爬案例分析与爬取实战