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

save() 和 restore() 保存的是绘图上下文的“设置快照”,不是画布图像本身——画完的东西不会被撤回,改过的 fillStyle、旋转角度、裁剪区这些才会还原。
哪些状态会被 save() 记住?
每次调用 save(),以下所有当前值都会被打包进栈:
- 变换矩阵(
translate()、rotate()、scale()累积后的结果) - 当前裁剪路径(由
clip()建立的区域) - 样式类属性:
fillStyle、strokeStyle、globalAlpha、globalCompositeOperation - 线条类设置:
lineWidth、lineCap、lineJoin、miterLimit - 文本相关:
font、textAlign、textBaseline - 阴影参数:
shadowOffsetX、shadowOffsetY、shadowBlur、shadowColor
注意:canvas.width/canvas.height、已绘制的像素、getImageData() 返回的数据,统统不保存。
restore() 调用失败的常见错误
报错 DOMException: The operation is not supported 通常是因为:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
-
restore()次数 >save()次数(栈空了还弹) - 在未调用
save()的上下文中直接restore()(比如刚拿到ctx就 restore) - 跨函数调用时漏掉配对:例如函数 A 调用了
save(),但没在内部restore(),而调用方也没补上
调试建议:在关键位置加 console.log(ctx.__stackDepth)(需自行打补丁)或用 try/catch 包裹 restore() 并打印调用栈。
嵌套使用时的执行顺序陷阱
状态栈是后进先出(LIFO),但容易误判“恢复到哪一步”。例如:
ctx.save(); // A(初始) ctx.rotate(0.2); ctx.save(); // B(含旋转) ctx.translate(100, 0); ctx.save(); // C(含旋转+平移) // 绘图 ctx.restore(); // 回到 B ctx.restore(); // 回到 A ctx.restore(); // 报错!栈已空
关键点:
- 每个
save()都新建一个独立快照,不是“覆盖”前一个 - 不能靠“数括号”来配对,要按实际执行流跟踪栈深度
- 封装绘图函数时,务必在函数入口
save()、出口restore(),否则副作用会泄漏
最常被忽略的一点:save()/restore() 不解决重绘逻辑,它只管“状态隔离”。如果你在动画循环里反复 save() 却忘了 clearRect(),旧图形依然留在画布上——状态干净了,画面未必干净。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










