
虽然 canvas 的功能非常强大,但是它本身只是一个 HTML 标签而已。与 canvas 的交互无非就是鼠标的点击、移动,手指的触摸、移动,键盘事件等等。本文将以鼠标在 canvas 绘制签名为例,简单说一下一些常用的交互实现方式
通过哪些事件实现签名功能?
对于一个元素而言,我们知道常用的鼠标事件有以下这些:
- mousedown
- mouseup
- mouseover
- mousemove
- mouseenter
- mouseout
- …
那么要实现一个签名的功能,我们需要使用到哪些事件呢?很显然,签名其实就是追踪鼠标移动的过程,所以mousemove是必不可少的
同时签名时,不能只要鼠标移动就绘制,而是需要我们先点击鼠标左键,且释放就不再绘制,所以也需要用到mousedown和mouseup事件来监听鼠标是否按下
由于签名必须在 canvas 中进行,因此如果鼠标在按下状态时离开了 canvas,我们也需要停止绘制,这就要用到mouseout事件监听鼠标是否移出了 canvas 区域
在确定我们需要使用的基本事件以后,就可以开始进行实操了
签名功能的代码编写
首先在页面上放入一个 canvas 标签并获取到该标签,为其添加相应的事件:
// signature.html
<canvas id="canvas"></canvas>
// signature.js
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
// 为 canvas 设置宽高,这个 800*400 的区域就是签名区域
canvas.width = 800
canvas.height = 400
// 为 canvas 添加事件
canvas.onmousedown = mouseDown
canvas.onmouseup = mouseUp
canvas.onmousemove = mouseMove
canvas.onmouseout = mouseOut
function mouseDown (e) {...}
function mouseUp (e) {...}
function mouseMove (e) {...}
function mouseOut (e) {...}
下面就开始编写三个鼠标事件对应的交互代码:
// 用一个变量判断当前是否可以绘制签名
let canDraw = false
// 用一个常量记录 canvas 当前的布局信息
const canvasRect = canvas.getBoundingClientRect()
function mouseDown (e) {
// 调用事件的 preventDefault() 方法可以阻止默认事件的触发
// 绘制签名可以不用这个方法
// e.preventDefault()
// 鼠标按下可以开始绘制签名
canDraw = true
ctx.beginPath()
// 设置签名的线条宽度为 10
ctx.lineWidth = 10
// 设置签名的颜色为蓝色
ctx.strokeStyle = 'blue'
// 设置签名线条的起始位置和线条拐角位置为圆形
ctx.lineCap = ctx.lineJoin = 'round'
// 获取鼠标位置相对于 canvas 上为位置
// 由于 e 上的鼠标位置信息是相对于整个窗口的,所以要在 canvas 上绘制出正确的路径,需要将鼠标的位置转换成它在 canvas 上的位置信息
const position = getMouse2CanvasPosition({x: e.clientX, y: e.clientY})
// 将鼠标移动到开始位置
ctx.moveTo(position.x, position.y)
}
function mouseUp (e) {
// 鼠标松开停止绘制签名
canDraw = false
ctx.closePath()
}
function mouseOut (e) {
canDraw = false
ctx.closePath()
}
function mouseMove (e) {
// 如果当前不能绘制签名则直接返回
if (!canDraw) return
// 开始绘制签名
// 获取鼠标移动后相对于 canvas 的位置
const position = getMouse2CanvasPosition({x: e.clientX, y: e.clientY})
// 将位置移动到目标位置
ctx.lineTo(position.x, position.y)
// 绘制线条
ctx.stroke()
}
// 用来计算鼠标在 canvas 上绘制的点的位置
function getMouse2CanvasPosition (position) {
return {
x: position.x - canvasRect.x,
y: position.y -canvasRect.y
}
}
通过上面的代码我们就可以实现一个简单的签名 demo 了,但是上面代码并不是完全没有问题的:
- 鼠标的位置信息使用
clientX
clientY
是否完全没有问题?这两个属性是相对于窗口的位置,而不是整个页面的位置,如果页面存在滚动条,那这两个属性获取到的位置信息绘制出来的签名就存在问题了。那如果不用这两个属性又可以用什么呢?感兴趣的小伙伴可以自己尝试看看 - 我们实际的签名是都粗细笔锋的,而 demo 中的签名却是粗细始终相同。鼠标模拟绘制或许不能像这样简单的就能完美复制出来,但是还是可以进行一些简单的优化来达到笔画粗细的改变的,其思路是通过鼠标移动的快慢来改变线条的粗细
- 上面的代码如果移植到移动端是否能够完美适配?如果不能该如何修改呢?移动端我们又该使用那些事件来进行与 canvas 的交互呢?
当然,canvas 交互并不是上面 demo 中这样简单的,对于那些用 canvas 来制作的大型游戏,它们的交互可是非常复杂的。但是我们可以先迈出 canvas 交互的一小步,然后才会有后面不断地沉淀与积累

网友评论