Canvas 状态管理 语法糖 canvas.withSave() {}
1、Canvas 状态管理
canvas.withSave() 是一个扩展函数,它内部会调用 canvas.save() 和 canvas.restore(),确保 Canvas 状态在闭包结束后自动恢复。
2. 避免状态泄漏
如果不使用 withSave(),手动调用 save() 和 restore() 容易出错:
- 忘记调用 restore() 会导致 Canvas 状态混乱
- 多次调用 save() 而不对应 restore() 会耗尽 Canvas 状态栈
3. 代码安全性
// 错误的方式 - 容易忘记 restore()canvas.save()// ... 绘制代码 ...// 如果这里抛出异常,restore() 永远不会被调用// 正确的方式 - 自动管理状态canvas.withSave() {// ... 绘制代码 ...// 即使抛出异常,状态也会自动恢复}
4. 作用域清晰
withSave() 闭包明确界定了需要特殊 Canvas 状态的代码范围:
- 闭包内的代码使用变换后的 Canvas
- 闭包外的代码使用原始 Canvas
5. 变换的局部性
在这个例子中:
canvas.withSave() {translate(offsetX, offsetY) // 平移scale(scaleFactor, scaleFactor, cx, cy) // 缩放// 绘制矩形 - 使用变换后的坐标系for (rect in rects) {drawRect(...)}} // 自动恢复原始状态// 这里绘制子视图 - 使用原始坐标系super.dispatchDraw(canvas)
6. 为什么需要变换
- 平移 (translate):移动整个画布,实现视图的拖拽效果
- 缩放 (scale):以中心点为基准缩放,实现缩放效果
- 矩形绘制:在变换后的坐标系中绘制,这样矩形就会跟随视图的变换
7. 为什么子视图绘制在闭包外
super.dispatchDraw(canvas) 需要在原始坐标系中执行,因为:
- 子视图的布局已经考虑了变换
- 子视图有自己的绘制逻辑
- 避免双重变换导致的问题
这种设计确保了:
- 矩形绘制使用变换后的坐标系
- 子视图绘制使用原始坐标系
- Canvas 状态自动管理,避免错误