save()和restore()保存恢复绘图上下文状态而非图像,包括变换矩阵、裁剪路径、fillstyle等样式属性;canvas尺寸、已绘制像素及getimagedata()数据不保存;restore()调用次数超过save()次数会报domexception错误。

save() 和 restore() 管理的是绘图上下文的“状态快照”,不是图像或像素——你平移、旋转、缩放后画的东西不会消失,但下次 fillRect() 用的坐标系、颜色、透明度会按栈顶状态还原。
哪些状态会被压入栈?
每次调用 save(),以下当前值全部打包进栈:
- 变换矩阵(
translate()、rotate()、scale()、transform()累积后的结果) - 裁剪路径(由
clip()建立的区域) - 样式属性:
fillStyle、strokeStyle、globalAlpha、lineWidth、font、textAlign、shadowColor、shadowOffsetX等 - 合成模式:
globalCompositeOperation
不保存的内容必须手动处理:canvas.width/height、已绘制的像素、getImageData() 结果、以及任何 JS 变量或函数作用域里的状态。
restore() 报 DOMException 怎么排查?
错误信息通常是 DOMException: The operation is not supported 或直接提示栈空。常见原因:
-
save()调用次数少于restore()(比如只调一次,却在循环里连用三次restore()) - 条件分支中漏配对:if 里有
save(),else 里没对应逻辑,或反之 - 异步回调中误用:比如
setTimeout(() => ctx.restore(), 100),但外层同步代码早已执行完所有restore() - 嵌套过深:旧版 Safari 在 >15 层时可能丢状态,建议控制在 10 层内
Chrome/Firefox 的 Canvas Inspector 可查看当前栈深度,调试时建议打开验证是否匹配。
模块化绘图时怎么安全封装 save/restore?
典型场景是绘制多个带独立变换的图标、文字或组件,关键原则是「自包含、成对、就近」:
- 每个绘制函数内部自己调用
save()和restore(),不依赖外部状态 - 避免包裹单条命令(如
ctx.save(); ctx.fillRect(); ctx.restore();),纯开销无意义 - 若只需重置变换(比如回到原始坐标系),优先用
ctx.setTransform(1, 0, 0, 1, 0, 0),比restore()更轻量 - 高频动画中慎用——每帧多次
save()会增加内存与栈操作开销,可考虑离屏 canvas 预渲染
示例:
function drawRotatedBox(ctx, x, y, angle) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
ctx.fillStyle = '#2563eb';
ctx.fillRect(-20, -10, 40, 20);
ctx.restore(); // 这里还原后,后续 drawRotatedBox 调用互不影响
}
最容易被忽略的一点:没调用 save() 就 restore() 不报错,而是静默失败——你以为恢复了,其实什么都没发生,图形错位可能拖到几轮绘制后才暴露。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











