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

3,013 阅读4分钟

前端训练营:1v1私教,终身辅导计划,帮你拿到满意的 offer 已帮助数百位同学拿到了中大厂 offer

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. 为了完成画笔切换,我们需要监听 selectchange 事件,从而修改 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实现签字板的完整代码,大家可以直接组合代码进行使用,最终展示的结果如下:

前端训练营:1v1私教,终身辅导计划,帮你拿到满意的 offer 已帮助数百位同学拿到了中大厂 offer