在这个例子中Canvas.Context Save and Restore的目的是什么?

时间:2013-05-06 23:55:43

标签: html5 html5-canvas

This page在HTML5画布中显示了一些动画。如果查看source of the scroller,则会在清除矩形并在动画后恢复它时保存上下文。如果我用另一个ctx.clearRect(0, 0, can.width, can.height语句替换restore语句,则无效。我认为恢复正在恢复已清除的矩形,但它似乎恢复了更多信息。下一帧需要的额外信息是什么?

我不是在寻找保存和恢复的HTML5教科书定义,但我想了解这个具体示例中为什么需要它们。

更新

在我不想获得save()和restore()定义的问题中我已经提到的答案令人沮丧。我已经知道Save()保存上下文的状态,Restor()e恢复它。我的问题很具体。为什么在将所有Save do保存为空画布时,以示例方式使用restore()。为什么还原空白画布与清除它相同?

2 个答案:

答案 0 :(得分:38)

画布状态并不是它所绘制的内容。它是一组属性,用于定义 工具 的当前状态,用于绘制 next 事情。

Canvas是一种立即模式位图。 像MS Paint一样。一旦它在那里,它就在那里,所以没有必要"保存"当前的图像数据,因为这就像保存整个JPEG一样,每次进行更改时,每一帧......

...不,你保存的状态是指定用于绘制NEXT事物的坐标方向,尺寸标度,颜色等的状态(以及之后的所有事情,直到你改变这些值为止手)。

var canvas = document.createElement("canvas"),
    easel  = canvas.getContext("2d");

easel.fillStyle = "rgb(80, 80, 120)";
easel.strokeStyle = "rgb(120, 120, 200)";

easel.fillRect(x, y, width, height);
easel.strokeRect(x, y, width, height);

easel.save();  // stores ALL current status properties in the stack

easel.rotate(degrees * Math.PI / 180); // radians
easel.scale(scale_X, scale_Y); // any new coordinates/dimensions will now be multiplied by these
easel.translate(new_X, new_Y); // new origin coordinates, based on rotated orientation, multiplied by the scale-factor

easel.fillStyle = "gold";
easel.fillRect(x, y, width, height); // completely new rectangle
// origin is different, and the rotation is different, because you're in a new coordinate space

easel.clearRect(0, 0, width, height); // not even guaranteed to clear the actual canvas, anymore
easel.strokeRect(width/2, height/2, width, height); // still in the new coordinate space, still with the new colour


easel.restore(); // reassign all of the previous status properties
easel.clearRect(0, 0, width, height);

假设您在堆栈中只有一个状态更改,那最后一行,现在您的画布'以前的状态已经恢复,应该已经成功地清除了自己(尽管有亚像素的诡计)。

正如你所看到的,它与擦除画布非常相似 事实上,它与删除它完全没有任何关系。

它与想要画一些东西,做基本的轮廓和清晰的颜色/样式,然后手动在顶部的较小细节的颜色写,然后手动写回所有样式的方式以前,为了下一个对象,然后继续扫描... ...

相反,保存将被重用的一般状态,为较小的细节创建新状态,并返回到一般状态,而不必每次都对其进行硬编码,或者编写setter函数来设置常用值。画布一遍又一遍(重置缩放/旋转/仿射变换/颜色/字体/线宽/基线对齐/等)。

在您的确切示例中,如果您要引起注意,您会发现唯一可以改变的是step的价值。

他们为画布设置了一堆值的状态(颜色/字体/等) 然后他们救了。那么,他们节省了什么? 你看起来不够深。他们实际上保存了 默认翻译(即原始世界空间中的原点= 0,0)
但你没看到他们定义它? 这是因为它被定义为默认值。

他们然后增加第1步像素(实际上,他们首先执行此操作,但在第一次循环后它并不重要 - 请留在我这里)。
然后他们为0,0设置了一个新的原点(即:从现在开始,当他们键入0,0时,新的原点将指向画布上完全不同的位置。)

原点等于x是画布的正中间,y等于当前步(即:像素1或像素2等等...以及为什么从0开始到开始时的差异1真的没关系。

然后他们做了什么? 他们恢复了。

那么,他们恢复了什么? ......好吧,他们改变了什么?

他们将原点恢复到0,0 为什么呢?

那么,如果他们没有这样做会发生什么? 如果画布是500px x 200px,它在我们当前的屏幕空间中从0,0开始......那太棒了...
然后他们把它翻译成宽度/ 2,1,1 好的,所以现在当他们要求在0,0画出文字时,他们实际上是在250,1,

奇妙。但下次会发生什么?

现在他们按宽度/ 2,2翻译 你认为,那很好......对于0,0的抽奖调用将发生在250,2,因为他们已经将它设置为清除数字:canvas.width / 2 ,2

不。因为根据我们的屏幕,当前0,0实际上是250,1。一个翻译与之前的翻译相关......

...所以,现在你要告诉画布从它开始的当前坐标' 0,0然后向左转250,向下转2. 根据屏幕(就像一个窗口,看着地图,而不是地图,本身)我们现在向右500px,从我们开始的地方向下3个像素...而且只有一个框架有过去了。

因此,他们将地图的坐标恢复为与屏幕坐标相同的原点(并且旋转相同,缩放比例和歪斜等等),之前设置新的。

正如你可能猜到的那样,通过观察,现在,你可以看到文本实际上应该从上到下移动。不对,就像页面说的那样......

为什么这样?
当绘图命令在函数中为您提供xy时,为什么还要更改绘图上下文的坐标系?

如果你想在画布上画一幅画,并且你知道它的高度和宽度,以及你想要左上角的位置,为什么你不能这样做呢? :

easel.drawImage(myImg, x, y, myImg.width, myImg.height);

嗯,你可以。
你完全可以做到这一点。什么都没有阻止你。

事实上,如果你想让它在屏幕上放大,你可以只更新计时器上的xy,并将其称为一天。

但是,如果你正在画一个游戏角色呢?如果这个角色戴着帽子,戴着手套的手和大靴子,所有这些东西都与角色分开了怎么办?

首先,你要说"嗯,他站在世界的x和y,所以x加上他的手与他的身体相关的是x + body.x - hand.x ...或是那个加......"

...现在你已经为他所有的部分画了一个电话,这些部分看起来就像一个装满5年级数学作业的笔记本。

相反,你可以说:"他在这里。设置我的坐标,以便0,0正好位于我的中间"。现在您的绘制调用就像"我的右手是身体右侧6个像素,左手是左侧3个像素"。

当您完成绘制角色时,可以将原点设置回0,0然后可以绘制下一个角色。或者,如果你想尝试它,你可以根据从一个到另一个的增量从那里翻译到下一个字符的原点(这将为每个翻译节省一个函数调用)。然后,如果你一次只保存状态(原始状态),最后,你可以通过调用.restore返回0,0。

答案 1 :(得分:1)

上下文save()可以保存转换颜色等内容。然后,您可以更改上下文并将其还原为与保存时相同。它像堆栈一样工作,因此您可以将多个画布状态推送到堆栈并恢复它们。 http://html5.litten.com/understanding-save-and-restore-for-the-canvas-context/