canvas 的 save() 和 restore() 管理绘图上下文状态快照,控制“怎么画”而非“画了什么”;用于隔离局部变换、裁剪区域和临时样式,支持嵌套调用,漏用或误用会导致状态残留或报错。

Canvas 的 save() 和 restore() 不是“撤销画布内容”,而是管理绘图上下文的状态快照——它管的是“怎么画”,不管“画了什么”。用对了,能让你的绘图逻辑清晰、模块化、无副作用;用错了,容易状态残留或报错。
局部变换:旋转/缩放/平移不影响后续绘制
当你想画一个旋转的图标、斜体文字或缩放后的图表,又不希望这些变换影响后面所有图形时,就该用 save/restore 隔离坐标系:
- 先
ctx.save()记录当前坐标系和样式 - 调用
translate()、rotate()或scale() - 绘制目标图形(比如一个旋转的矩形)
- 再
ctx.restore()回到原始坐标系和样式
漏掉 restore(),后续所有 fillRect()、fillText() 都会沿错误方向偏移或变形。
独立裁剪区域:为某组图形设置专属可视范围
想只让某几个图形显示在圆形区域内,其他图形照常绘制?裁剪路径必须隔离:
ctx.save()- 用
beginPath()+arc()绘制圆形路径 - 调用
clip()激活裁剪 - 绘制被裁剪的图形(如带渐变的扇形)
-
ctx.restore()—— 裁剪区域失效,后续绘制不再受限制
不配对使用,裁剪会持续生效,导致后面所有内容都被意外裁掉。
临时样式切换:避免全局污染
给某个按钮加半透明描边,但不想影响其他图形的 globalAlpha 或 strokeStyle:
ctx.save()- 修改
globalAlpha = 0.6、strokeStyle = '#ef4444' - 调用
strokeRect()或stroke() -
ctx.restore()—— 所有样式自动回退到之前值
尤其适合封装组件函数,比如 drawIcon(ctx, x, y) 内部自己 save/restore,调用者完全不用操心状态清理。
嵌套绘图与调试要点
多个 save 可嵌套形成后进先出栈,适合复杂 UI 分层绘制:
- 每层模块(如仪表盘中的指针、刻度、背景)可各自 save/restore
- Chrome/Firefox 开发者工具的 Canvas Inspector 可查看当前栈深度,验证是否匹配
- restore 调用次数多于 save 会抛
DOMException(栈空),常见于条件分支遗漏或异步回调中误用 - 若只需重置坐标系(回到原始矩阵),用
ctx.setTransform(1, 0, 0, 1, 0, 0)比 restore 更轻量
不复杂但容易忽略。











