基于VMAJSTER代码改造的Canvas签名板如何实现离画布自动停止书写
问题根因
持续跨画布绘制的现象,是两个机制共同作用的结果:
- 浏览器原生鼠标拖拽捕获规则:只要在画布元素上触发
mousedown且未松开按键,后续的mousemove、mouseup事件都会被定向派发回当初按下鼠标的画布元素,不会因光标移出元素就中断触发 - 原代码未设置任何绘制状态的中断触发点:既没有监听光标离开画布的事件,也没有校验鼠标坐标是否仍在画布有效范围内,只要事件触发就会持续向笔迹数组追加点位并绘制曲线
实现思路
所有中断绘制的场景统一复用同一套终止逻辑,避免代码重复:
- 抽离原本绑定在
mouseup事件上的终止绘制流程,封装为独立的endDrawing方法 - 给画布绑定
mouseleave事件,光标移出画布边界时自动调用终止方法 - 在核心绘制流程
onPaint开头增加坐标范围校验,一旦检测到鼠标坐标超出画布有效像素范围,立即终止绘制 - 兼容触摸场景:同步增加触摸点位置校验、
touchcancel中断逻辑,保证移动端体验一致
核心代码改动
- 封装通用的终止绘制方法
// 通用终止绘制方法 function endDrawing() { // 移除移动事件的绘制监听 tmp_canvas.removeEventListener("mousemove", onPaint, false); // 将临时画布上的未提交笔迹合并到正式画布 ctx.drawImage(tmp_canvas, 0, 0); // 清空临时画布,为下一次绘制做准备 tmp_ctx.clearRect(0, 0, tmp_canvas.width, tmp_canvas.height); // 重置存储笔迹点的数组 ppts = []; }
- 替换原有
mouseup事件的回调逻辑,直接复用封装好的方法
// 鼠标松开时执行终止流程 tmp_canvas.addEventListener("mouseup", endDrawing, false);
- 新增光标离开画布的自动中断逻辑
这里注意不要用mouseout事件替代mouseleave,前者会在光标移动到画布内部嵌套的子元素上时误触发中断,后者仅在光标完全移出画布边界时才会触发。
// 光标移出画布时自动停止绘制 tmp_canvas.addEventListener("mouseleave", function() { // 仅在处于绘制状态时触发终止,避免误操作 if (ppts.length > 0) endDrawing(); }, false);
- 在核心绘制流程中增加边界校验,作为双保险
哪怕因为浏览器拖拽锁定机制导致光标移出后仍触发mousemove事件,只要检测到坐标越界就立即终止,避免笔迹画出画布范围:
var onPaint = function(e) { // 坐标越界直接终止绘制 if ( mouse.x < 0 || mouse.y < 0 || mouse.x > canvas.width || mouse.y > canvas.height ) { endDrawing(); // 主动派发一次全局mouseup事件,解除浏览器的鼠标拖拽锁定状态 document.dispatchEvent(new MouseEvent("mouseup")); return false; } // 以下保持原有绘制逻辑不变 ppts.push({ x: mouse.x, y: mouse.y }); // ... 原有的曲线平滑绘制、点位存储逻辑 }
注意事项
- 坐标判断使用canvas实际的像素宽高(即代码中给canvas元素设置的
width/height属性值),不要用CSS设置的显示宽高做判断,否则会出现边界判定不准的问题 - 主动派发全局
mouseup的步骤不能省略,否则浏览器会一直保持拖拽锁定状态,后续在画布上操作时可能出现绘制异常
内容的提问来源于stack exchange,提问作者Change
相关产品推荐
相关产品推荐

