在 Canvas 中,我们介绍了画布的基本概念和使用方式,现在,我们来用它实现一个基本的手写板 - 你可以在 Tablet 查看源码。
> 原生 JS 实现一下喽 ~
1
2
3
4
5
6
7
8
9
10
11
12
|
<!-- 画布 -->
<canvas
id="cvs"
width="600" height="300"
ontouchstart="touchstart(event)"
ontouchmove="touchmove(event)"
ontouchend="touchend(event)"
onmousedown="mousedown(event)"
onmousemove="mousemove(event)"
onmouseup="mouseup(event)"
>
</canvas>
|
一般来说,手写板什么的多在移动设备(触摸)上使用,只涉及 touch***
相关事件,当然,如上所示,PC 端使用 mouse***
事件模拟即可。
下面,让我们看一下具体实现吧(以 mouse***
事件为例)。
基本思路
手写板应用的核心,就是使用 Canvas 实时绘制路径(path),我们先来简单回顾一下这方面的知识,如下:
beginPath()
- 新建一条路径,路径一旦创建成功,图形绘制命令被指向到路径上生成路径
moveTo(x, y)
- 把画笔移动到指定的坐标 (x, y),相当于设置路径的起始点坐标
lineTo(x, y)
- 添加一个新点,然后创建从该点到画面中最后指定点的线条
closePath()
- 闭合路径之后,图形绘制命令又重新指向到上下文中
stroke()
- 通过线条来绘制图形轮廓
通过以上绘制路径的方法,我们使用 mousestart
结合 beginPath()
和 moveTo(x, y)
方法,开始绘制路径,并在 mousemove
事件触发的过程中结合 lineTo(x, y)
和 stroke()
实时绘制路径。
核心解析
1. 先准备一下吧
1
2
|
let cvs = document.querySelector('#cvs'); // 获取画布
let ctx = cvs.getContext('2d'); // 上下文
|
2. 看看相关的事件
1
2
3
4
5
6
7
8
9
10
|
function mousedown(e) {
drawstart(e.pageX - cvs.offsetLeft, e.pageY - cvs.offsetTop);
}
// function drawstart(x, y) {
// document.body.classList.add('body-fix'); // 书写时禁止页面滚动
// ctx.beginPath();
// ctx.moveTo(x, y);
// }
|
mousedown
和 mousemove
事件中,我们可以方便获取鼠标指针相对于其第一个父级元素(带有 position
属性)的相对位置 (x, y)
,如果没有,就是相对于 body
了(本例中即是如此)。
![[assets/Pasted image 20230526112628.png|500]]
1
2
3
4
5
6
7
8
9
10
|
function mousemove(e) {
if (e.buttons === 1) { // 鼠标左键按下时
drawmove(e.pageX - cvs.offsetLeft, e.pageY - cvs.offsetTop);
}
}
// function drawmove(x, y) {
// ctx.lineTo(x, y);
// ctx.stroke();
// }
|
实时坐标的获取同上,此外需要注意的是,这里我们限制了 仅当鼠标左键按下时 才会绘制路径,否则,绘制出来的路径只会是个鬼画符。当然,如果你是在触摸设备中使用 touch***
事件,则不存在这个问题。
不同浏览器对于鼠标事件的监听指示可能有所不同,Chrome 中,当 e.buttons
为 1
时,表示左键是按下状态,如果你想做兼容,请查看相关文档。
1
2
3
4
5
6
7
8
9
|
function mouseup(e) {
drawend();
}
// function drawend() {
// // ctx.closePath()
// document.body.classList.remove('body-fix'); // 书写完成恢复页面滚动
// }
|
你可能已经注意到了,在 mousedown
和 mouseup
中,我们针对 body
元素做了一些类别修改 - 添加/删除 body-fix
,它有什么作用呢?
1
2
3
|
.body-fix {
overflow: hidden;
}
|
![[assets/Pasted image 20230526112641.png|500]]
很简单,就是为了防止在书写签名时页面滚动,导致你写不成字 ~ 当然,别忘记在 mouseup
时,移除该类,否则,你就滚动不了页面喽。
辅助功能
1. 生成签名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
// 生成签名
function expCvs() {
let src = cvs.toDataURL('image/png', 1);
/*
* canvas.toDataURL(type, encoderOptions)
* 返回:
* - 该方法返回一串 URI 字符串(canvas 中图像数据的 base64 编码)
*
* 参数:
* - type:图像格式,默认为"image/png"
* - encoderOptions:数值为 0 ~ 1,表示图片质量,仅在 type 为 "image/jpeg" 或 "image/webp" 时有效
*
* 其他:
* png 默认生成图片无背景,jpeg 默认生成图片为黑色背景
* 如果需要白色背景,可以在绘制前先绘制背景:
* ctx.fillStyle = '#fff';
* ctx.fillRect(0, 0, canvas.width, canvas.height);
*/
console.log(src);
img.src = src;
}
|
Canvas 可以方便地生成图片格式(base64)文件 - 通过 toDataURL
方法,如此,我们就可以方便的传递数据或将其作为图片标签的 src
属性使用。
这里注意,做为签名来说,我们通常需要生成背景透明的图像,所以默认即为 png
格式的。
2. 清除签名
1
2
3
4
5
|
// 清除签名
function clrCvs() {
ctx.clearRect(0, 0, 600, 300);
img.src = '';
}
|
很简单,直接使用 clearRect
清空一个画布就可以了。
3. 选择颜色
1
2
3
4
5
|
// 选择签名颜色
function selectColor(e) {
console.log(e.target.value);
ctx.strokeStyle = e.target.value;
}
|
可选功能,用来设置画笔颜色,当然,还有其他设置项,你完全可以按需添加。
结语
基本原理,就是讲的这些,具体项目中实现可能会稍有改变,但难不到你的,对吧 🥳
参考链接