canvas 的 save() 和 restore() 管理绘图环境状态栈,保存变换矩阵、裁剪路径、样式、线条、文本、阴影等属性,不保存像素和路径;必须成对使用以避免错误或状态污染,支持嵌套与模块化绘图。

Canvas 的 save() 和 restore() 不是撤销已画内容,而是管理绘图环境的“快照”与“回退”。它们操作的是一个后进先出的状态栈,只保存可变的上下文属性,不碰像素数据。
哪些状态会被保存?
每次调用 save(),以下全部当前值会被打包压入栈:
- 变换矩阵(由
translate、rotate、scale或setTransform累积生成) - 裁剪路径(由
clip()设定的有效区域) - 样式设置:包括
fillStyle、strokeStyle、globalAlpha、globalCompositeOperation - 线条属性:如
lineWidth、lineCap、lineJoin、miterLimit - 文本设置:如
font、textAlign、textBaseline - 阴影参数:
shadowOffsetX、shadowOffsetY、shadowBlur、shadowColor
注意:已绘制的图形、canvas 尺寸、像素数据、当前路径(path)和绘图点(current point)均不被保存。
为什么必须成对使用?
save() 把状态推入栈,restore() 从栈顶弹出并完全还原——这是严格的一对一关系:
- 多调用
restore()而没有对应save(),会触发DOMException错误 - 漏掉
restore(),会导致后续所有绘制继承残留的变换或样式,比如文字莫名旋转、颜色错乱、裁剪区持续生效 - 嵌套调用天然支持模块化:函数内部
save()→ 绘图 →restore(),对外零副作用
典型安全用法模式
核心原则:在可能污染全局状态的操作前保存,操作完成后立即恢复。
- 画旋转文字:
save()→rotate()→fillText()→restore() - 局部裁剪:
save()→ 绘制裁剪路径 →clip()→ 绘图 →restore() - 临时混合模式:
save()→globalCompositeOperation = 'overlay'→ 绘图 →restore() - 图标组件封装:内部自行管理状态,调用者无需关心坐标系是否被改动
变换顺序与状态栈的关系
变换本身是矩阵叠加,顺序直接影响结果(矩阵乘法不可交换);而 save/restore 是隔离这种影响的屏障:
- 先
translate(100, 0)再rotate(45°):图形平移后绕新原点旋转 - 先
rotate(45°)再translate(100, 0):平移方向也被旋转了 - 用
save/restore包裹任一组,就能确保它不影响其他图形的坐标逻辑











