在HTML5 / ECMAScript / JavaScript中进行图形处理时,一个非常常见的模式是执行context.save(),应用坐标转换,然后执行context.restore()。不幸的是,即使绘制上下文被丢弃并重新创建,HTML5的设计似乎仍然会像转换一样继续作为画布的一部分。如果在转换坐标时发生错误,绘图上下文将处于混乱状态"永远"。
如果使用保存/恢复的每一段代码都被try
块保护,它可以确保在退出时恢复上下文。但是,如果使用save
的任何代码抛出异常而不恢复上下文,那么将导致调用上下文的任何恢复操作恢复不正确的状态。
有没有干净的做法,如:
var savedContext = contextCapturer.capture(context);
try
{
... code that might call a save without restore
}
finally
{
savedContext.restore();
}
我认为有可能让一个上下文保护对象修改上下文的保存/恢复方法,这样他们就可以保留一个可以用来确保事物平衡的计数,但我不确定如何编写这样的代码,以便即使它调用,或被其他同样的代码调用,也能正常工作。 restore
执行后,保存的上下文不一定可用;什么是必要的,如果"尝试"阻止执行例如五次保存和三次恢复,savedContext.restore()
操作必须在上下文中执行两次额外的恢复,以弥补不平衡。
为了澄清,我在这里考虑像showFramed方法这样的事情;给定一个期望在inRect
定义的范围内绘制的过程,它会转换坐标和剪辑,使其出现在outRect
定义的范围内(在这种情况下也会绘制一个黄色边框)。
function showFramed(ctx, inRect, outRect, bColor, bWidth, proc)
{
ctx.save();
ctx.strokeStyle=bColor;
ctx.lineWidth=bWidth;
ctx.beginPath();
ctx.rect(outRect[0],outRect[1],outRect[2],outRect[3]);
ctx.stroke();
ctx.restore();
ctx.save();
ctx.clip();
ctx.translate(outRect[0],outRect[1]);
ctx.scale(outRect[2]/inRect[2], outRect[3]/inRect[3]);
ctx.translate(-inRect[0],-inRect[1]);
proc();
ctx.restore();
}
function showScreen(ctx)
{
var i;
ctx.beginPath();
for (i=0; i<=160; i+=10)
{
ctx.moveTo(i,0);
ctx.lineTo(0,160-i);
ctx.lineTo(i/2+80,i/2+80);
ctx.lineTo(160-i,160);
}
ctx.stroke();
}
function showScreen2(ctx)
{
showScreen(ctx);
ctx.save();
ctx.strokeStyle="#007F00";
showFramed(ctx, [0,0,160,160], [20,90,60,60], "#FFFF00", 3,
function() {showScreen(ctx); });
ctx.strokeStyle="#7F0000";
showFramed(ctx, [0,40,80,80], [90,90,60,60], "#FFFF00", 3,
function() {showScreen(ctx); });
ctx.restore();
}
function demo1()
{
var c=document.getElementById("canv");
var ctx=c.getContext("2d");
ctx.strokeStyle="#0000FF";
showScreen2(ctx);
}
function demo2()
{
var c=document.getElementById("canv");
var ctx=c.getContext("2d");
ctx.strokeStyle="#00FF00";
showFramed(ctx, [0,0,160,160], [80,10,60,60], "#FFFF00", 3,
function() {showScreen2(ctx); });
}
&#13;
<canvas id="canv" Width=160 height=160></canvas>
<button onClick="demo1()">Demo1</button>
<button onClick="demo2()">Demo2</button>
&#13;
drawFramed
方法不知道在输入时适用哪种转换。如果从传入的过程中抛出异常,则不应该吞下它,但我建议showFramed
应该将上下文恢复到输入之前的状态;不应该吗?