理解 canvas 的 save() 和 restore()

本来是打算自己写 save() 和 restore(),但是发现一篇博客对于 save() 和 restore() 的理解很清晰易懂,就翻译过来了。 原文链接:https://html5.litten.com/understanding-save-and-restore-for-the-canvas-context/

--------------------------------- O(∩∩)O 以下是正文 O(∩∩)O-------------------------------**

首先,曾几何时我也无法理解 save() 和 restore() 的真正用意。其实呢,非常简单,这里有几个栗子能帮助大家更好的理解这两个方法。

我们先看下官方文档对 save 和 restore 的定义:

每个 canvas 的 context 都包含一个保存绘画状态的栈。以下内容都属于绘画状态:

  • 当前的 transformation matrix(变换矩阵)
  • 当前的裁剪区域
  • strokeStyle、fillStyle、globalAlpha、lineWidth、lineCap、lineJoin、miterLimit、shadowOffsetX、shadowOffsetY、shadowBlur、shadowColor、globalCompositeOperation、font、textAlign、textBaseline,这些属性的当前值

**

当前路径和当前的位图并不属于绘画状态。当前路径是永久的一直存在的,要清楚或重置的话,只能通过 beginPath 方法。而当前位图是属于 canvas 的属性,而不是 context 的。 **

context.save() 会把当前的状态推入栈中。 **

context.restore() 会从栈中取出最顶部的状态,context 就还原到取出的状态。

因为一个 canvas只能有一个 2d context, 所以 save 和 restore 被广泛用于各种情况。其中最普遍的就是用于 transformation(变换矩阵)。

一个栗子告诉你 save() 和 restore() 是如何在 transformations(多次矩阵变换) 中发挥作用的。

当我们使用 transformation (矩阵变换)时,整个 context 的坐标系统都会发生变换。通常变换之后,我们下一步可能会想让坐标系统还原成变换之前正常的状态。我们要再次通过 transformation 反着让坐标系统还原吗,这显然不是一个快速可靠的方法。此时,我们就可以在变换之前,先通过 save 把正常的坐标系通过状态保存下来入栈,做完矩阵变换后在通过 restore 从栈中取出我们之前保存的状态。我们看个栗子后应该能更加清晰一些。

刚开始我们通过 canvascontext.save() 保存下当前的绘画状态,并且拷贝一份当前状态推入绘画状态栈中。 可以看到下图是一个正常坐标系统,我们保存下来当前状态。 然后我们做矩阵变换,可以看到坐标系统是发生了变化的。 接着我们画完我们想要的形状并且填充一些颜色。 现在我们想画更多的形状在画布上,但是我们不想使用当前的矩阵变换,我们就通过 restore() 把最近的状态从绘画状态栈里取出来,也就是我们之前保存的有正常坐标系统的状态。那么当前的状态就变成我们 save 之前的状态了。 可以看到背景的网格恢复到了正常的坐标系统的样子,但是我们后画的形状并没有变化,因为我们保存的状态只保存了 transformation 的状态,我们并没有设置其他想要保存的状态。

我们再来一个更直观的栗子

完整代码及注释

现在我们来看一个更加直观的栗子,来说明绘画状态栈与 save() 和 restore() 是如何配合运作的。





Canvas Test


This text is displayed if your browser does not support HTML5 Canvas.

分段代码、效果图、注释具体说明

接下来我们分段代码并结合效果图来说明,会更加清晰。

/* 我们画了一个橘色的矩形,设置了阴影 */
ctx.fillStyle = '#FA6900'; 
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
ctx.shadowBlur    = 4;
ctx.shadowColor   = 'rgba(204, 204, 204, 0.5)';      
ctx.fillRect(0,0,15,150);
/* 把当前状态(fillStyle 橘色,阴影属性),推入到状态栈中,假设叫状态1 */
ctx.save();  

可以看到下图状态栈中多了一个状态,并且很贴心的颜色也与矩形一直,让人更好理解。

can1.gif
state1.gif
/* 然后我们画了一个 土黄色的矩形,设置了阴影 */
ctx.fillStyle = '#E0E4CD'; 
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowBlur    = 4;
ctx.shadowColor   = 'rgba(204, 204, 204, 0.5)';      
ctx.fillRect(30,0,30,150);
/* 把当前状态(fillStyle 土黄色,阴影属性),推入到状态栈中,假设叫状态2 */
ctx.save(); 

我们看到状态栈中有两个状态了:状态1(橘色的)、状态2(土黄色的)

can2.gif
state2 (1).gif
/* 然后我们画了一个 翠绿色的矩形,设置了阴影 */
ctx.fillStyle = '#A7DBD7'; 
ctx.shadowOffsetX = 15;
ctx.shadowOffsetY = 15;
ctx.shadowBlur    = 4;
ctx.shadowColor   = 'rgba(204, 204, 204, 0.5)';      
ctx.fillRect(90,0,45,150);
/* 把当前状态(fillStyle 翠绿色,阴影属性),推入到状态栈中,假设叫状态3 */
ctx.save();

我们看到状态栈中有三个状态了:状态1(橘色的)、状态2(土黄色的)、状态3(翠绿色的)

can3.gif
state3 (1).gif
/*我们从状态栈顶部取出状态3,当前绘制状态就变成了状态3了(翠绿色的)*/
ctx.restore();
/*画一个翠绿色的圆*/
ctx.beginPath();
ctx.arc(185, 75, 22, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill(); 
can4.gif
state3 (1).gif
/*我们从状态栈顶部取出状态3,当前绘制状态就变成了状态2了(土黄色的)*/
ctx.restore();
/*画一个土黄色的圆*/
ctx.beginPath();
ctx.arc(260, 75, 15, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
can5.gif
state2.gif
/*我们从状态栈顶部取出状态3,当前绘制状态就变成了状态1了(橘色色的)*/
ctx.restore();
/*画一个橘色的圆*/
ctx.beginPath();
ctx.arc(305, 75, 8, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
can6.gif
state1.gif

--------------------------------- O(∩∩)O 正文结束 O(∩∩)O--------------------------------**

这样下来是不是很简单?!希望我有翻译清楚。欢迎指正。

我是“南丘啊南丘”,希望大家在平凡的日子里好好学习,热爱生活,日日是好日!

点赞.gif

你可能感兴趣的:(理解 canvas 的 save() 和 restore())