当前位置: 首页 > 文档资料 > HTML5 Canvas 实战 >

4.7 使用栈处理多重变换

优质
小牛编辑
122浏览
2023-12-01

通过HTML5的画布API,我们可以很好的处理变换,现在我们将进一步探讨画布的状态栈,看看它对变换的作用。在第2章 图形及组合中,我们曾经探讨过状态栈,它是画布API中一个非常强大,而往往被忽视的特性。虽然状态栈可以帮助管理样式,但是最常用的,还是保存和恢复变换状态。本节,我们将进行多重变换,在每重变换过程中,都会保存画布状态,然后在恢复每个状态后,再绘制一系列矩形,看看其效果。

多重变换
图4-9 多重变换

绘制步骤

按照以下步骤,构造一个有四个不同状态的状态栈,然后,在弹出每个状态之后,绘制矩形:

1. 定义画布上下文,及矩形的尺寸:

window.onload = function(){
  var canvas  = document.getElementById("myCanvas");
  var context = canvas.getContext("2d");
  var rectWidth  =  150;
  var rectHeight =  75;

2. 把当前变换状态,即默认状态,压入栈顶,并平移上下文:

  context.save();  //保存状态1
  context.translate(canvas.width  /  2, canvas.height  /  2);

3. 把当前变换状态,即平移状态,压入栈顶,并旋转上下文:

  context.save();  //保存状态2
  context.rotate(Math.PI  /  4);

4. 把当前变换状态,即平移状态和旋转状态,压入栈顶,并缩放上下文:

  context.save();  //保存状态3
  context.scale(2,  2);

5. 绘制该矩形:

  // 绘制该矩形
  context.fillStyle  = "blue";
  context.fillRect(-rectWidth  /  2,  -rectHeight  /  2, rectWidth, rectHeight);

6. 通过弹出当前状态,来从状态栈恢复出前一个状态,并绘制一个红色矩形:

  context.restore();  // 恢复状态3
  context.fillStyle  = "red";
  context.fillRect(-rectWidth  /  2,  -rectHeight  /  2, rectWidth, rectHeight);

7. 通过弹出当前状态,来从状态栈中恢复出前一个状态,并绘制一个黄色矩形:

  context.restore();  //恢复状态2
  context.fillStyle  = "yellow";
  context.fillRect(-rectWidth  /  2,  -rectHeight  /  2, rectWidth, rectHeight);

8.    通过弹出当前状态,来从状态栈中恢复出前一个状态,并绘制一个绿色矩形:

  context.restore();  //恢复状态1
  context.fillStyle  = "green";
  context.fillRect(-rectWidth  /  2,  -rectHeight  /  2, rectWidth, rectHeight);
};

9. 在HTML文档的body部分嵌入canvas标签:

<canvas id="myCanvas" width="600" height="250" style="border:1px solid black;">
</canvas>

工作原理

本节,我们进行了三次变换,一次平移、一次旋转、一次缩放,在每次变换时,都执行save()操作把变换状态压栈。当蓝色矩形绘制完成,它是被居中的、被旋转的、并被缩放的。此时,状态栈中有四个状态(从栈底到栈顶):

1. 默认状态

2. 平移状态

3. 平移和旋转状态

4. 当前状态(平移、旋转、缩放状态)

蓝色矩形绘制完成后,我们调用restore()方法来弹出栈顶的状态,把画布状态恢复到第三个状态,此时画布上下文是被平移和旋转的状态。红色矩形绘制完成后,你可以看到它是被平移和旋转的,但没有被缩放。其次,我们再一次调用restore()方法来弹出栈顶的状态,把画布状态恢复到第二个状态,此时画布上下文只有平移状态。我们再绘制黄色矩形,它的确仅仅被平移。最后,我们最后一次调用restore()方法来弹出栈顶的状态,并回到默认状态。当我们绘制绿色矩形,它出现在初始位置,因为没有对它应用任何变换。

使用状态栈,我们可以在变换状态之间进行跳转,从而不必不断的把恢复状态栈到默认状态,再分别平移每个元素。另外,我们也可以使用保存-恢复组合把一小段坐标变换的代码包裹起来,使其不影响后面绘制的图形。