Canvas涂鸦画板撤销功能通过记录路径指令而非像素快照实现,用history和undone栈管理操作,监听鼠标事件构建路径,重绘时按路径属性复原;需节流mousemove、校验初始化尺寸、处理移动端事件并注意导出限制。

Canvas 涂鸦画板加撤销功能,核心在于把每次绘制操作存为“快照”或“指令”,而不是直接修改像素——这样撤销时才能精准回退,不丢失细节。
用路径记录代替像素快照
每次鼠标按下到抬起之间画的是一条路径(Path),记录起点、终点、线宽、颜色等参数比截图省内存也更灵活。Canvas 本身不自动保存历史,需手动维护一个操作栈。
- 监听 mousedown、mousemove、mouseup,在 mousedown 时新建一个空路径对象
- mousemove 中用 lineTo() 追加点,同时调用 stroke() 实时显示(可选)
- mouseup 后把完整路径推入 history 数组,清空当前路径变量
撤销与重做靠栈式管理
history 是主栈,存放所有已完成的绘制动作;undone 存已撤销但还能重做的动作,类似浏览器前进后退。
- 点击撤销:从 history 弹出最后一项,push 到 undone,然后 clearRect() 重绘画布,再遍历剩余 history 重放
- 重做:从 undone 弹出,push 回 history,同样重绘全部
- 注意:重绘时别用 save()/restore() 套嵌太深,容易性能下降;直接用路径对象的属性重设 context 状态即可
优化体验的小细节
纯路径重绘快,但用户拖得快时可能漏点。加个节流或采样插值能更顺滑;另外,撤销后光标位置若还在画布上,松开鼠标会意外触发新路径,需在 mouseup 里加判断。
立即学习“前端免费学习笔记(深入)”;
- mousemove 节流建议 10–15ms,避免高频写点导致卡顿
- 每条路径加个 timestamp 或 id,方便调试和未来扩展(比如按时间过滤)
- 撤销到空时禁用撤销按钮,重做到顶时禁用重做按钮,用 disabled 属性控制 UI 状态
导出与初始化兼容性
导出图片用 toDataURL('image/png') 即可,但注意跨域图片不能 drawImage 到 canvas,否则导出会失败。新画布初始化时记得设置 width/height CSS 和 Canvas 属性一致,否则缩放失真。
- 设置 canvas.width 和 canvas.height 用实际像素值,不要只靠 CSS 拉伸
- 导出前可临时放大 canvas(如 ×2)再绘制,提升图片清晰度,导完再恢复
- 移动端需监听 touchstart/touchmove/touchend,并阻止默认滚动行为
不复杂但容易忽略。关键不是画得多炫,而是操作可逆、状态可控、响应及时。



















