如何实现一个签名功能——与 canvas 的简单交互

虽然 canvas 的功能非常强大,但是它本身只是一个 HTML 标签而已。与 canvas 的交互无非就是鼠标的点击、移动,手指的触摸、移动,键盘事件等等。本文将以鼠标在 canvas 绘制签名为例,简单说一下一些常用的交互实现方式

通过哪些事件实现签名功能?

对于一个元素而言,我们知道常用的鼠标事件有以下这些:

  1. mousedown
  2. mouseup
  3. mouseover
  4. mousemove
  5. mouseenter
  6. mouseout

那么要实现一个签名的功能,我们需要使用到哪些事件呢?很显然,签名其实就是追踪鼠标移动的过程,所以mousemove是必不可少的

同时签名时,不能只要鼠标移动就绘制,而是需要我们先点击鼠标左键,且释放就不再绘制,所以也需要用到mousedownmouseup事件来监听鼠标是否按下

由于签名必须在 canvas 中进行,因此如果鼠标在按下状态时离开了 canvas,我们也需要停止绘制,这就要用到mouseout事件监听鼠标是否移出了 canvas 区域

在确定我们需要使用的基本事件以后,就可以开始进行实操了

签名功能的代码编写

首先在页面上放入一个 canvas 标签并获取到该标签,为其添加相应的事件:

// signature.html


// 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 了,但是上面代码并不是完全没有问题的:

  1. 鼠标的位置信息使用 clientX clientY 是否完全没有问题?这两个属性是相对于窗口的位置,而不是整个页面的位置,如果页面存在滚动条,那这两个属性获取到的位置信息绘制出来的签名就存在问题了。那如果不用这两个属性又可以用什么呢?感兴趣的小伙伴可以自己尝试看看
  2. 我们实际的签名是都粗细笔锋的,而 demo 中的签名却是粗细始终相同。鼠标模拟绘制或许不能像这样简单的就能完美复制出来,但是还是可以进行一些简单的优化来达到笔画粗细的改变的,其思路是通过鼠标移动的快慢来改变线条的粗细
  3. 上面的代码如果移植到移动端是否能够完美适配?如果不能该如何修改呢?移动端我们又该使用那些事件来进行与 canvas 的交互呢?

当然,canvas 交互并不是上面 demo 中这样简单的,对于那些用 canvas 来制作的大型游戏,它们的交互可是非常复杂的。但是我们可以先迈出 canvas 交互的一小步,然后才会有后面不断地沉淀与积累

如何实现一个签名功能——与 canvas 的简单交互_第1张图片
扫码关注前端周记公众号

你可能感兴趣的:(如何实现一个签名功能——与 canvas 的简单交互)