前端实现电子签名(web、移动端)通用的实战过程

 更新时间:2022年12月12日 10:11:02   作者:桃小瑞  
电子签名通俗来说就是通过技术手段实现在电子文档上加载电子形式的签名,下面这篇文章主要给大家介绍了关于前端实现电子签名(web、移动端)通用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

脚本之家 / 编程助手:解决程序员“几乎”所有问题!
脚本之家官方知识库 → 点击立即使用

前言

在现在的时代发展中,从以前的手写签名,逐渐衍生出了电子签名。电子签名和纸质手写签名一样具有法律效应。电子签名目前主要还是在需要个人确认的产品环节和司法类相关的产品上较多。

举个常用的例子,大家都用过钉钉,钉钉上面就有电子签名,相信大家这肯定是知道的。

那作为前端的我们如何实现电子签名呢?其实在html5中已经出现了一个重要级别的辅助标签,是啥呢?那就是canvas

什么是canvas

Canvas(画布)是在HTML5中新增的标签用于在网页实时生成图像,并且可以操作图像内容,基本上它是一个可以用JavaScript操作的位图(bitmap)Canvas 对象表示一个 HTML 画布元素 -。它没有自己的行为,但是定义了一个 API 支持脚本化客户端绘图操作。

大白话就是canvas是一个可以在上面通过javaScript画图的标签,通过其提供的context(上下文)Api进行绘制,在这个过程中canvas充当画布的角色。

如何使用

canvas给我们提供了很多的Api,供我们使用,我们只需要在body标签中创建一个canvas标签,在script标签中拿到canvas这个标签的节点,并创建context(上下文)就可以使用了。

1
2
3
4
5
6
7
8
9
10
...
<body>
    <canvas></canvas>
</body>
<script>
    // 获取canvas 实例
    const canvas = document.querySelector('canvas')
    canvas.getContext('2d')
</script>
...

步入正题。

实现电子签名

知道几何的朋友都很清楚,线有点绘成,面由线绘成。

多点成线,多线成面。

所以我们实际只需要拿到当前触摸的坐标点,进行成线处理就可以了。

在body中添加canvas标签

在这里我们不仅需要在在body中添加canvas标签,我们还需要添加两个按钮,分别是取消保存(后面我们会用到)。

1
2
3
4
5
6
7
<body>
    <canvas></canvas>
    <div>
        <button>取消</button>
        <button>保存</button>
    </div>
</body>

添加文件

我这里全程使用js进行样式设置及添加。

1
2
3
4
5
6
7
8
9
// 配置内容
    const config = {
        width: 400, // 宽度
        height: 200, // 高度
        lineWidth: 5, // 线宽
        strokeStyle: 'red', // 线条颜色
        lineCap: 'round', // 设置线条两端圆角
        lineJoin: 'round', // 线条交汇处圆角
    }

获取canvas实例

这里我们使用querySelector获取canvas的dom实例,并设置样式和创建上下文。

1
2
3
4
5
6
7
8
9
// 获取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')

基础设置

我们将canvas的填充色为透明,并绘制填充一个矩形,作为我们的画布,如果不设置这个填充背景色,在我们初识渲染的时候是一个黑色背景,这也是它的一个默认色。

1
2
3
4
5
6
7
8
9
// 设置填充背景色
ctx.fillStyle = 'transparent'
// 绘制填充矩形
ctx.fillRect(
    0, // x 轴起始绘制位置
    0, // y 轴起始绘制位置
    config.width, // 宽度
    config.height // 高度
);

上次绘制路径保存

这里我们需要声明一个对象,用来记录我们上一次绘制的路径结束坐标点及偏移量。

  • 保存上次坐标点这个我不用说大家都懂;
  • 为啥需要保存偏移量呢,因为鼠标和画布上的距离是存在一定的偏移距离,在我们绘制的过程中需要减去这个偏移量,才是我们实际的绘制坐标。
  • 但我发现chrome中不需要减去这个偏移量,拿到的就是实际的坐标,之前在微信小程序中使用就需要减去偏移量,需要在小程序中使用的朋友需要注意这一点哦。
1
2
3
4
5
6
7
// 保存上次绘制的 坐标及偏移量
const client = {
    offsetX: 0, // 偏移量
    offsetY: 0,
    endX: 0, // 坐标
    endY: 0
}

设备兼容

我们需要它不仅可以在web端使用,还需要在移动端使用,我们需要给它做设备兼容处理。我们通过调用navigator.userAgent获取当前设备信息,进行正则匹配判断。

1
2
// 判断是否为移动端
const mobileStatus = (/Mobile|Android|iPhone/i.test(navigator.userAgent))

初始化

这里我们在监听鼠标按下(mousedown)(web端)/触摸开始(touchstart)的时候进行初始化,事件监听采用addEventListener

1
2
// 创建鼠标/手势按下监听器
window.addEventListener(mobileStatus ? "touchstart" : "mousedown", init)

三元判断说明: 这里当mobileStatustrue时则表示为移动端,反之则为web端,后续使用到的三元依旧是这个意思。

声明初始化方法

我们添加一个init方法作为监听鼠标按下/触摸开始的回调方法。

这里我们需要获取到当前鼠标按下/触摸开始的偏移量和坐标,进行起始点绘制。

Tips:web端可以直接通过event中取到,而移动端则需要在event.changedTouches[0]中取到。

这里我们在初始化后再监听鼠标的移动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 初始化
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)
}

绘制

这里我们添加绘制draw方法,作为监听鼠标移动/触摸移动的回调方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 绘制
const draw = event => {
    // 获取当前坐标点位
    const { pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
    // 修改最后一次绘制的坐标点
    client.endX = pageX
    client.endY = pageY
 
    // 根据坐标点位移动添加线条
    ctx.lineTo(pageX , pageY )
 
    // 绘制
    ctx.stroke()
}

结束绘制

添加了监听鼠标移动/触摸移动我们一定要记得取消监听并结束绘制,不然的话它会一直监听并绘制的。

这里我们创建一个cloaseDraw方法作为鼠标弹起/结束触摸的回调方法来结束绘制并移除鼠标移动/触摸移动的监听。

canvas结束绘制则需要调用closePath()让其结束绘制

1
2
3
4
5
6
7
// 结束绘制
const cloaseDraw = () => {
    // 结束绘制
    ctx.closePath()
    // 移除鼠标移动或手势移动监听器
    window.removeEventListener("mousemove", draw)
}

添加结束回调监听器

1
2
// 创建鼠标/手势 弹起/离开 监听器
window.addEventListener(mobileStatus ? "touchend" :"mouseup", cloaseDraw)

ok,现在我们的电子签名功能还差一丢丢可以实现完了,现在已经可以正常的签名了。

我们来看一下效果:

取消功能/清空画布

我们在刚开始创建的那两个按钮开始排上用场了。

这里我们创建一个cancel的方法作为取消并清空画布使用

1
2
3
4
5
// 取消-清空画布
const cancel = () => {
    // 清空当前画布上的所有绘制内容
    ctx.clearRect(0, 0, config.width, config.height)
}

然后我们将这个方法和取消按钮进行绑定

1
<button onclick="cancel()">取消</button>

保存功能

这里我们创建一个save的方法作为保存画布上的内容使用。

将画布上的内容保存为图片/文件的方法有很多,比较常见的是blobtoDataURL这两种方案,但toDataURL这哥们没blob强,适配也不咋滴。所以我们这里采用a标签 ➕ blob方案实现图片的保存下载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 保存-将画布内容保存为图片
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()
    })
}

然后我们将这个方法和保存按钮进行绑定

1
<button onclick="save()">保存</button>

我们将刚刚绘制的内容进行保存,点击保存按钮,就会进行下载保存

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<!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>

各内核和浏览器支持情况

Mozilla 程序从 Gecko 1.8 (Firefox 1.5 (en-US)) 开始支持 <canvas>。它首先是由 Apple 引入的,用于 OS X Dashboard 和 Safari。Internet Explorer 从 IE9 开始支持<canvas> ,更旧版本的 IE 中,页面可以通过引入 Google 的 Explorer Canvas 项目中的脚本来获得<canvas>支持。Google Chrome 和 Opera 9+ 也支持 <canvas>

小程序中提示

在小程序中我们如果需呀实现的话,也是同样的原理哦,只是我们需要将创建实例和上下文的Api进行修改,因为小程序中是没有dom,既然没有dom,哪来的操作dom这个操作呢。

总结

到此这篇关于前端实现电子签名(web、移动端)通用的文章就介绍到这了,更多相关前端实现通用电子签名内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

蓄力AI

微信公众号搜索 “ 脚本之家 ” ,选择关注

程序猿的那些事、送书等活动等着你

原文链接:https://juejin.cn/post/7174251833773752350

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!

相关文章

  • 三剑客:offset、client和scroll还傻傻分不清?

    三剑客:offset、client和scroll还傻傻分不清?

    这篇文章主要给大家介绍了三剑客:offset,client和scroll的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • JavaScript之instanceof方法手写示例详解

    JavaScript之instanceof方法手写示例详解

    这篇文章主要为大家介绍了JavaScript之instanceof方法手写示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • JavaScript实现购物车图片局部放大预览效果

    JavaScript实现购物车图片局部放大预览效果

    这篇文章主要为大家详细介绍了JavaScript如何通过canvas简单实现购物车图片放大预览效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-03-03
  • showModalDialog在谷歌浏览器下会返回Null的解决方法

    showModalDialog在谷歌浏览器下会返回Null的解决方法

    showModalDialog的返回值在IE、火狐下面都能够获取返回值,但是在谷歌浏览器下面会返回Null,下面有个不错的解决方法,感兴趣的朋友可以参考下
    2013-11-11
  • JavaScript的内置对象Math和字符串详解

    JavaScript的内置对象Math和字符串详解

    这篇文章主要为大家介绍了JavaScript的内置对象Math和字符串,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-11-11
  • 基于JS实现数字动态变化显示效果附源码

    基于JS实现数字动态变化显示效果附源码

    我们经常看到液晶电子表样式,数字动态显示,动态变化的在指定元素内显示数字。怎么实现效果呢?下面小编给大家带来了基于JS实现数字动态变化显示效果 ,感兴趣的朋友一起看看吧
    2019-07-07
  • Javascript页面跳转常见实现方式汇总

    Javascript页面跳转常见实现方式汇总

    这篇文章主要介绍了Javascript页面跳转常见实现方式,结合实例汇总分析了JavaScript常用的七种页面跳转实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-11-11
  • cnblogs 代码高亮显示后的代码复制问题解决实现代码

    cnblogs 代码高亮显示后的代码复制问题解决实现代码

    cnblogs是比较有名的技术博客基地,很多技术达人都在里面发布技术文章, 不过由于代码不利于复制,因为页面里面有pre标签等问题
    2011-12-12
  • JS访问对象两种方式区别解析

    JS访问对象两种方式区别解析

    这篇文章主要介绍了JS访问对象两种方式区别解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • JavaScript 跨域之POST实现方法

    JavaScript 跨域之POST实现方法

    本篇文章主要介绍了JavaScript 跨域之POST实现方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05

最新评论