签字板很难吗?纯 JS 实现一个!

开发 前端
我说:“这种功能很简单呀,目前市面上有很多开源的库,比如:signature_pad 就可以直接引入实现”。但是,该同学说自己公司的项目比较特殊,尽量不要使用 第三方的库,所以想要自己实现,那怎么办呢?

Hello,大家好,我是 Sunday。

前段时间有位同学问我:“公司项目中需要增加一个签字板的功能”,问我如何进行实现。

我说:“这种功能很简单呀,目前市面上有很多开源的库,比如:signature_pad 就可以直接引入实现”。

但是,该同学说自己公司的项目比较特殊,尽量不要使用 第三方的库,所以想要自己实现,那怎么办呢?

没办法!只能帮他实现一个了.

签字板实现逻辑

签字板的功能实现其实并不复杂,核心是 基于 canvas 的 2d 绘制能力,监听用户 鼠标 或者 手指 的移动行为,完成对应的 线绘制。

所以,想要实现签字板那么必须要有一个 canvas,先看 html 的实现部分:

html

<body>
    <!-- 画板 -->
    <canvas id="signature-pad" width="400" height="200"></canvas>
    <!-- 控制器 -->
    <div class="controls">
        <select id="stroke-style">
            <option value="pen">钢笔</option>
            <option value="brush">毛笔</option>
        </select>
        <button id="clear">清空</button>
    </div>
    <script src="script.js"></script>
</body>

我们可以基于以上代码完成 HTML 布局,核心是两个内容:

  1. canvas 画布:它是完成签字板的关键
  2. controls 控制器:通过它可以完成 画笔切换 以及 清空画布 的功能

css

css 相对比较简单,大家可以根据自己的需求进行调整就可以了,以下是 css 大家可以作为参考:

* {
  margin: 0;
  padding: 0;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  height: 100vh;
  width: 100vw;
  background-color: #f0f0f0;
  overflow: hidden;
}

canvas {
  border: 1px solid #000;
  background-color: #fff;
}

.controls {
  margin-top: 10px;
  display: flex;
  gap: 10px;
}

button,
select {
  padding: 5px 10px;
  cursor: pointer;
}

js

js 部分是整个签字板的核心,我们需要在这里考虑较多的内容,比如:

  1. 为了绘制更加平滑,我们需要使用 ctx.quadraticCurveTo 方法完成平滑过渡
  2. 为了解决移动端手指滑动滚动条的问题,我们需要在 move 事件中通过 e.preventDefault() 取消默认行为
  3. 为了完成画笔切换,我们需要监听 select 的 change 事件,从而修改 ctx.lineWidth 画笔

最终得到的 js 代码如下所示(代码中提供的详细的注释):

document.addEventListener('DOMContentLoaded', function () {
 // 获取 canvas 元素和其 2D 上下文
 var canvas = document.getElementById('signature-pad')
 var ctx = canvas.getContext('2d')
 var drawing = false // 标志是否正在绘制
 var lastX = 0,
  lastY = 0 // 保存上一个点的坐标
 var strokeStyle = 'pen' // 初始笔画样式

 // 开始绘制的函数
 function startDrawing(e) {
  e.preventDefault() // 阻止默认行为,避免页面滚动
  drawing = true // 设置为正在绘制
  ctx.beginPath() // 开始新路径

  // 记录初始点的位置
  const { offsetX, offsetY } = getEventPosition(e)
  lastX = offsetX
  lastY = offsetY
  ctx.moveTo(offsetX, offsetY) // 移动画笔到初始位置
 }

 // 绘制过程中的函数
 function draw(e) {
  e.preventDefault() // 阻止默认行为,避免页面滚动
  if (!drawing) return // 如果不是在绘制,直接返回

  // 获取当前触点位置
  const { offsetX, offsetY } = getEventPosition(e)

  // 使用贝塞尔曲线进行平滑过渡绘制
  ctx.quadraticCurveTo(
   lastX,
   lastY,
   (lastX + offsetX) / 2,
   (lastY + offsetY) / 2
  )
  ctx.stroke() // 实际绘制路径

  // 更新上一个点的位置
  lastX = offsetX
  lastY = offsetY
 }

 // 停止绘制的函数
 function stopDrawing(e) {
  e.preventDefault() // 阻止默认行为
  drawing = false // 结束绘制状态
 }

 // 获取事件中触点的相对位置
 function getEventPosition(e) {
  // 鼠标事件或者触摸事件中的坐标
  const offsetX = e.offsetX || e.touches[0].clientX - canvas.offsetLeft
  const offsetY = e.offsetY || e.touches[0].clientY - canvas.offsetTop
  return { offsetX, offsetY }
 }

 // 鼠标事件绑定
 canvas.addEventListener('mousedown', startDrawing) // 鼠标按下开始绘制
 canvas.addEventListener('mousemove', draw) // 鼠标移动时绘制
 canvas.addEventListener('mouseup', stopDrawing) // 鼠标抬起停止绘制
 canvas.addEventListener('mouseout', stopDrawing) // 鼠标移出画布停止绘制

 // 触摸事件绑定
 canvas.addEventListener('touchstart', startDrawing) // 触摸开始绘制
 canvas.addEventListener('touchmove', draw) // 触摸移动时绘制
 canvas.addEventListener('touchend', stopDrawing) // 触摸结束时停止绘制
 canvas.addEventListener('touchcancel', stopDrawing) // 触摸取消时停止绘制

 // 清除画布的功能
 document.getElementById('clear').addEventListener('click', function () {
  ctx.clearRect(0, 0, canvas.width, canvas.height) // 清空整个画布
 })

 // 修改笔画样式的功能
 document
  .getElementById('stroke-style')
  .addEventListener('change', function (e) {
   strokeStyle = e.target.value // 获取选中的笔画样式
   updateStrokeStyle() // 更新样式
  })

 // 根据 strokeStyle 更新笔画样式
 function updateStrokeStyle() {
  if (strokeStyle === 'pen') {
   ctx.lineWidth = 2 // 细线条
   ctx.lineCap = 'round' // 线条末端圆角
  } else if (strokeStyle === 'brush') {
   ctx.lineWidth = 5 // 粗线条
   ctx.lineCap = 'round' // 线条末端圆角
  }
 }

 // 初始化默认的笔画样式
 updateStrokeStyle()
})

以上就是 纯JS实现签字板的完整代码,大家可以直接组合代码进行使用,最终展示的结果如下:

图片 图片

责任编辑:武晓燕 来源: 程序员Sunday
相关推荐

2022-07-13 15:31:29

手绘板canvas鸿蒙

2023-04-17 09:08:27

CSS计时器

2024-03-13 08:21:53

冒泡排序动画

2011-11-03 09:13:27

JavaScript

2020-08-07 10:40:56

Node.jsexpress前端

2011-10-25 09:28:30

Node.js

2024-09-02 00:03:00

tabs组件CSS

2017-03-28 21:03:35

代码React.js

2022-04-06 18:29:58

CSSJS输入框

2018-01-29 21:56:28

Bug程序程序员

2009-07-02 10:02:40

JSP程序

2022-04-14 20:43:24

JavaScript原型链

2020-10-16 15:06:59

开发技术方案

2021-05-13 20:20:40

Java架构代码

2020-04-08 08:35:20

JavaScript模块函数

2012-07-10 16:09:54

App盈利

2013-03-18 10:31:22

JS异常

2022-03-04 14:17:08

JS工具库录音

2023-12-27 14:05:00

关系型数据库产品

2020-08-17 15:25:25

HTMLPython网页
点赞
收藏

51CTO技术栈公众号