画布状态并不是在其上绘制的内容。它是定义当前状态的属性堆栈tools用于绘制next thing.
Canvas 是即时模式位图。
就像 MS Paint 一样。一旦它在那里,它就在那里,所以没有必要“保存”当前的图像数据,因为这就像保存整个 JPEG,每次你进行更改,每一帧......
...不,您保存的状态将决定您用来绘制下一个事物(以及此后的所有事物,直到您手动更改这些值)的坐标方向、尺寸比例、颜色等的状态。
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
.
他们已经为画布设置了一堆值的状态(颜色/字体/等)。
然后他们保存。那么,他们拯救了什么?
你看的不够深入。他们实际上拯救了默认翻译(即:原始世界空间中的 origin=0,0).
但你没看到他们定义它吗?
那是因为它被定义为默认值。
然后,他们增加第 1 步像素(实际上,他们首先执行此操作,但在第一个循环之后并不重要 - 请留在此处)。
然后他们为 0,0 设置一个新的原点(即:从现在开始,当他们输入0,0
新的原点将指向画布上完全不同的位置)。
该原点等于 x 是画布的正中间,y 等于当前步骤(即:像素 1 或像素 2 等...以及为什么从 0 开始和从 1 开始之间的差异实际上并不存在没关系)。
然后他们做什么呢?
他们恢复。
那么,他们恢复了什么?
……好吧,他们改变了什么?
他们正在将原点恢复到 0,0
Why?
好吧,如果他们不这样做,会发生什么?
如果画布是 500px x 200px,并且它在我们当前的屏幕空间中从 0,0 开始......那太好了......
然后他们将其转换为宽度/2, 1
好的,现在当他们要求在 0,0 处绘制文本时,他们实际上会在 250, 1 处绘制
精彩的。但下次会发生什么呢?
现在他们按宽度/2, 2 进行翻译
你认为,好吧,那很好...... 0,0 的绘制调用将在 250, 2 发生,因为他们已将其设置为清除数字:canvas.width/2, 2
没有。因为根据我们的屏幕,当前的 0,0 实际上是 250,1。并且一个翻译是相对于其前一个翻译的......
...所以现在您告诉画布从当前坐标 0,0 开始,向左 250,向下 2。
根据屏幕(就像一个窗口,查看地图,而不是地图本身),我们现在位于右侧 500 像素处,距离开始位置向下 3 像素...并且只过去了一帧。
因此,在设置新坐标之前,他们将地图的坐标恢复为与屏幕坐标相同的原点(并且旋转相同,比例、倾斜等......)。
正如您可能猜到的,现在通过查看它,您可以看到文本实际上应该从上到下移动。不是从右到左,就像页面上说的那样......
为什么要这样做?
当绘图命令给你一个x
and y
就在函数中吗?
如果你想在画布上画一幅画,并且你知道它的高度和宽度,以及你想要左上角的位置,为什么不能这样做:
easel.drawImage(myImg, x, y, myImg.width, myImg.height);
好吧,你可以。
你完全可以做到这一点。没有什么可以阻止你。
事实上,如果你想让它在屏幕上缩放,你只需更新x
and y
在计时器上,今天就到此为止。
但如果你画的是游戏角色呢?如果角色有一顶帽子、戴着手套的手和大靴子,并且所有这些东西都与角色分开绘制怎么办?
所以首先你会说“好吧,他站在世界上的 x 和 y 处,所以 x 加上他的手相对于他的身体的位置将是 x + body.x - hand.x...或者是那个加号。 ..”
...现在您对他的所有部件进行了绘制调用,这些部件看起来都像一本装满 5 年级数学作业的笔记本。
相反,你可以说:“他是here。设置我的坐标,使 0,0 位于我的家伙的中间”。现在您的绘制调用就像“我的右手距离身体右侧 6 个像素,我的左手距离左侧 3 个像素”一样简单。
绘制完角色后,您可以将原点设置回 0,0,然后可以绘制下一个角色。或者,如果您想尝试它,您可以根据从一个字符到另一个字符的增量,从那里翻译到下一个字符的起源(这将为您节省每次翻译的函数调用)。然后,如果您一直只保存一次状态(原始状态),那么最后,您可以通过调用返回到 0,0.restore
.