2.6 使用上下文栈来保存和恢复样式
在创建较为复杂的HTML画布应用时,你将发现你需要一种恢复之前样式的方法,以便在绘制过程中,不必设置或重置不同点的诸多样式属性。幸运的是,HTML5的画布API为我们提供了访问画布上下文状态栈的途径,状态栈允许我们保存和恢复上下文状态。本节,我们将通过保存上下文状态、设置全局透明度、绘制一个透明的圆、恢复状态栈到之前设置的全局透明度,并绘制一个不透明的矩形,来演示状态栈的工作原理。请看下图!
准备工作
在探讨画布状态栈之前,有必要了解一下栈这个数据结构的工作原理(如果你已经了解,可以直接跳到工作原理部分)。栈是一个后进先出(LIFO)的数据结构。栈有三个主要操作,分别是压栈(push)、弹栈(pop)、取栈顶元素(top)。当一个元素被压栈,它被添加到栈顶。当一个元素被弹栈,栈顶元素被从栈中移除。取栈顶元素操作只是简单地返回栈顶元素而已。
请看上图,它表示一个栈在所有动作下的状态。第1步,我们从包含一个元素"a"的栈出发。第2步,"b"元素被压栈。第3步,"c"元素被压栈。第4步,执行弹栈操作,该操作会移除最后压栈的元素。由于元素"c"位于栈顶,所以它是被移除的元素。第5步,我们再一次弹栈,该操作会移除最后压栈的元素。由于元素"b"位于栈顶,所以它是被移除的元素。
下一节我们将看到,当状态随着时间变化,而要保存栈的状态,再通过弹栈来恢复状态时,栈是一个很好的数据结构。
绘制步骤
按照以下步骤,在透明的圆之上,绘制一个不透明的矩形:
1. 定义2D画布上下文:
window.onload = function(){
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
2. 绘制矩形:
//绘制矩形
context.beginPath();
context.rect(150, 30, 130, 130);
context.fillStyle = "blue";
context.fill();
3. 调用save()方法保存上下文状态,使用globalAlpha属性设置画布的全局透明度,绘制圆,然后调用restore()方法恢复画布状态:
// 使用save-restore 组合包裹绘制圆的代码
context.save();
context.globalAlpha = 0.5; //设置全局透明度
context.beginPath();
context.arc(canvas.width / 2, canvas.height / 2, 70, 0, 2 * Math.PI, false);
context.fillStyle = "red"; context.fill();
context.restore();
4. 绘制另一个矩形(它将是非透明的),以表明画布上下文状态已经被恢复到设置全局透明度之前的状态:
//绘制另一个矩形
context.beginPath();
context.rect(canvas.width - (150 + 130), canvas.height - (30 + 130), 130, 130);
context.fillStyle = "green"; context.fill();
};
5. 在HTML文档的body部分嵌入canvas标签:
<canvas id="myCanvas" width="600" height="250" style="border:1px solid black;">
</canvas>
工作原理
从前面的代码可以看到,通过save-restore组合包裹绘制圆的代码,实质上我们把save()方法和restore()方法之间的样式包裹起来,这样一来,它们不影响后面绘制的图形。save-restore 组合可被认为是引入样式作用域的一种方式,跟JavaScript在函数中包含变量作用域的方式类似。尽管你可能会说:“噢,它听上去好像是把globalAlpha设置为1的一个更复杂的方法”。等一下,伙计。在现实世界中,一般情况下,你将会为不同部分的代码处理大量的不同样式的组合。在这种情况下,save-restore组合就是一个救星。如果没有save-restore组合,要编写复杂的HTML画布应用,就像是用一大块只使用全局变量的JavaScript代码,来构建一个复杂的Web应用。哎呀!
了解更多
在第4章 掌握变形中我们将会看到,状态栈的另一种常见用法是保存和恢复变形状态。
相关参考
- 第4章 使用栈处理多个变形