用 Canvas 实现个手写板

之前学了 canvas,现在线上签字那么流行,就想着自己实现一个玩玩

打算实现一个包含以下功能的写字板

  • 使用鼠标写字
  • 调整画笔粗细
  • 修改画笔颜色
  • 撤销功能
  • 清空画布的功能
  • 生成图片,下载签名

静态结构

静态结构很简单,就直接列出来不做解释了

HTML

字体大小:
字体颜色:

CSS

#settings {
    display: flex;
    margin: auto;
    margin-top: 100px;
    line-height: 27px;
    justify-content: center;
    gap: 20px;
}

#tablet {
    display: block;
    margin: auto;
    margin-top: 10px;
    background-color: #eee;
}

gap 属性可能有人不认识,可以设置 flex/grad 布局子元素的间隔

静态页面就是这么个样子,字体大小的选项是用 js 生成的

用 Canvas 实现个手写板_第1张图片

接下来我们来写 JS

功能实现

初始化 canvas

我们先初始化 canvas 和其 2d 上下文 ctx

设置线宽为 5,线的末端和连接处均为圆角

const tablet = document.getElementById('tablet')
const ctx = tablet.getContext('2d')
ctx.lineWidth = 5
ctx.lineCap = 'round'
ctx.lineJoin = 'round'

字体大小

然后我们生成字体大小的选项,允许 1-16px,默认为 5px

const fontSize = document.getElementById('fontSize')
for (let i = 1; i <= 16; i++) {
    const option = document.createElement('option')
    option.value = i
    option.innerText = i + 'px'
    fontSize.appendChild(option)
    if (i == 5) {
        option.selected = true
    }
}
fontSize.addEventListener('change', (e) => {
    ctx.lineWidth = e.target.value
})

字体颜色

从 input 读取颜色,设置线色

const fontColor = document.getElementById('fontColor')
fontColor.addEventListener('change', (e) => {
    ctx.strokeStyle = e.target.value
})

写字

写字分为三部分

  • 鼠标落下:开始写字,对应 mousedown 事件
  • 鼠标移动:正在写字,对应 mousemove 事件
  • 鼠标抬起:结束写字,对应 mouseup 事件

写字无非就是许多点连成的线,在写字过程中,记录鼠标上一点的位置,将其与当前点连起来

我个人习惯在鼠标按下时注册事件,鼠标松开时清除事件;还有一种实现方式是用一个变量控制 mouseover 是否执行

mouseup 事件绑定在 document 上,又加了一个 mouseenter 事件,使得鼠标离开画布再回来可以继续写。

let prex, prey // 记录上一点的位置
const mousedown = (e) => {
    if (e.button != 0) return // 不是左键直接返回
    prex = e.offsetX
    prey = e.offsetY
    ctx.beginPath() // 开始路径
    ctx.moveTo(prex, prey)
    // 注册事件
    tablet.addEventListener('mousemove', mousemove)
    document.addEventListener('mouseup', mouseup)
    tablet.addEventListener('mouseenter', mouseenter)
}
const mousemove = (e) => {
    ctx.lineTo(e.offsetX, e.offsetY)
    ctx.stroke()
}
const mouseup = () => {
    // 关闭路径
    ctx.closePath()
    // 清除事件
    tablet.removeEventListener('mousemove', mousemove)
    document.removeEventListener('mouseup', mouseup)
    tablet.removeEventListener('mouseenter', mouseenter)
}
const mouseenter = (e) => {
    // 从外部回到画布,改变上一点的位置,继续书写
    prex = e.offsetX
    prey = e.offsetY
    ctx.moveTo(prex, prey)
}
tablet.addEventListener('mousedown', mousedown)

用 Canvas 实现个手写板_第2张图片

撤销

想要实现撤销功能,就要保存之前的状态,可以在开始写字时保存此时的画布状态,在需要撤销时恢复

考虑到 imageData 内容较大,我们这里只实现了一步的撤销功能

let imageData // 图片数据
const mousedown = (e) => {
    imageData = ctx.getImageData(0, 0, tablet.width, tablet.height)
    ……
}

const undo = document.getElementById('undo')
undo.addEventListener('click', () => {
    ctx.putImageData(imageData, 0, 0)
})

清空画布

清空画布很简单,clearRect 就好了

const clear = document.getElementById('clear')
clear.addEventListener('click', () => {
    ctx.clearRect(0, 0, tablet.width, tablet.height)
})

下载图片

canvas 生成 DateURL,然后利用 a 标签下载

let a
const download = document.getElementById('download')
download.addEventListener('click', () => {
    if (!a) {
        a = document.createElement('a')
    }
    a.href = tablet.toDataURL('image/png')
    a.download = '签名.png'
    a.click()
})

完整代码





    
    
    Document
    



    
字体大小:
字体颜色:

结语

至此写字板就完成了,都是 canvas 的知识,最后借助了 a 标签实现图片下载。

如果喜欢或有所帮助的话,希望能点赞关注,鼓励一下作者。

如果文章有不正确或存疑的地方,欢迎评论指出。

你可能感兴趣的:(用 Canvas 实现个手写板)