Html Canvas - 翻译,旋转,剪辑 - 使用context.restore进行更快的恢复

时间:2014-07-17 14:55:20

标签: html canvas restore

几天前询问有关动画速度的问题后,stackoverflow帮会再一次解决了我的问题。然而,这导致了另一个问题。 [你知道的越多,你就越意识到你不知道。]

基本上我的画布状态变化越少,事情就越快。如果我只是更改fillStyle,那么使用ctx.save和ctx.restore是过度的,因为所有状态都已恢复。矫枉过正=慢。相反,只需将fillStyle的旧值保留在某个位置,并在完成后将其放回原位。

那么你如何为ctx.translate(x,y),ctx.rotate(angle)和ctx.clip()做这个呢?

如何在更改之前将这些人恢复到他们的状态而不必使用ctx.restore?

2 个答案:

答案 0 :(得分:3)

您可以使用负值进行转换。

ctx.translate(100,100);
// draw lots of stuff
ctx.translate(-100,-100);

ctx.scale(.75,.50);
// draw stuff
ctx.scale(-.75,-.50);

ctx.rotate(Math.PI/4);
// draw stuff
ctx.rotate(-Math.PI/4);

如果进行多次转换,则必须按相反顺序撤消

ctx.translate(100,100);
ctx.scale(.75,.50);    
ctx.rotate(Math.PI/4);

// draw lots of stuff

ctx.rotate(-Math.PI/4);
ctx.scale(-.75,-.50);
ctx.translate(-100,-100);

但是在翻译(移动)一些项目时,使用偏移而不是变换会更快。

strokeRect(20+100,20+100,50,30);
fillRect(20+100,20+100,50,30);

剪辑是半永久性的,因此您必须保存/恢复整个上下文状态以撤消剪辑:

context.save();
// define a clipping path
context.clip();
// draw stuff
context.restore();

使用变换矩阵完成变换。 Canvas允许您使用context.setTransform方法访问该矩阵。

scaleX=.75;
scaleY=.50;
skewX=0;
skewY=0;
translateX=100;
translateY=100;

context.setTransform(scaleX, skewX, skewY, scaleY, translateX, translateY);

// draw stuff

context.setTransform(-scaleX, -skewX, -skewY, -scaleY, -translateX, -translateY);

要设置旋转矩阵,您必须设置比例尺和组合。偏斜值如下:

var radianAngle=Math.PI/4;
var cos=Math.cos(radianAngle);
var sin=Math.sin(radianAngle);

context.setTransform(cos,sin,-sin,-cos,0,0);

// draw stuff

context.setTransform(-cos,-sin,sin,cos,0,0);

要与其他变换一起进行旋转,只需将旋转值添加到比例和倾斜值。

context.setTransform(scaleX+cos, skewX+sin, skewY-sin, scaleY-cos, translateX, translateY);

// draw stuff

context.setTransform(-scaleX-cos, -skewX-sin, -skewY+sin, -scaleY+cos, -translateX, -translate);

答案 1 :(得分:1)

只是为了纠正问题的假设:

错误:•使用save()/ restore()方法时保存/恢复整个上下文状态。

让我们谦虚:主要浏览器的开发者最有可能找到(并改进)30秒内想到的想法。所以事实是:

True:•保存上下文几乎不执行任何操作,并且还原仅适用于刚刚发生的情况。

如果有疑问,你可以查看代码,但是需要花一些时间来熟悉它(我用webKit的canvas =>确认了这个代码)。
但是查看关于这个主题的各种jsperf要容易得多:它们表明手动保存/恢复一个或两个属性时的收益是中等到小==>只有恢复了什么变化 当手动保存/恢复更多内容时,由于Javascript的开销,保存和恢复变得更快
http://jsperf.com/save-restore-vs-translate-twice/4

另一件事:谈论'矫枉过正'似乎非常夸张。不仅因为,如前所述,上下文的保存可能会更快,但也因为,最佳胜利是2倍,所以我们正在谈论自豪地采取2ns而不是4ns进行保存。这必须与平局所需的时间进行比较,而且很可能不值得。

最后两件事: •手动保存/恢复引起的bug风险('哎呀!我忘了恢复那个功能!') •可能发生的舍入误差(scale(x,x)=>然后缩放(1 / x,1 / x))

事实上,你可以节省时间,没有风险是:
1)批处理命令:只要有可能(这一切都取决于您的应用程序,真的),批处理所有需要给定上下文状态的命令。
2)同样,您可以定义阻止您保存/恢复上下文的约定/规则。例如:'在填充之前总是设置fillStyle'。这样你就不必担心当前的fillSyle。你在这里做的也很大程度上取决于你的应用程序(以及它是否使用外部API),但可以节省大量的绘图时间。

所以我的建议是仅对明显的简单情况使用手动保存/恢复(例如:您只需更改globalAlpha),并使用约定/规则将上下文状态更改降至最低。