Html画布-翻译,旋转,剪辑-使用上下文还原更快的恢复



前几天问了一个关于动画速度的问题,stackoverflow帮又一次解决了我的问题。然而,这又引出了另一个问题。[你知道的越多,你越意识到你不知道。]

基本上,画布的状态变化越少,事情就会越快。如果我只是改变填充样式,然后使用ctx。保存并点击。Restore是多余的,因为所有状态都被恢复了。过度杀戮=缓慢。相反,只需将fillStyle的oldvalue保存在某个地方,并在完成后将其放回。

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

我怎么能恢复这些家伙到他们的状态之前,我的变化而不必使用ctx.restore?

您可以通过使用负值来取消转换

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允许您使用上下文访问该矩阵。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);

纠正问题的假设:

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

让我们谦虚一点:一个在30秒内出现在脑海中的想法最有可能被主流浏览器的开发人员发现(并改进)。所以事实是:

True: •保存上下文几乎不做任何事情,还原只应用于刚刚发生的事情。

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

另一件事:谈论'overkill'似乎很夸张。不仅因为,如前所述,上下文的保存可能更快,而且还因为,最佳获胜是2X,所以我们正在自豪地谈论用2ns而不是4ns进行保存。这必须与抽签所花费的时间进行比较,并且很可能不值得。

最后两点:•手动保存/恢复('哎呀!我忘记在那个函数中恢复了!")。
•可能出现的舍入误差(scale(x,x) => then scale(1/x, 1/x))

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

所以我的建议是只在明显的简单情况下使用手动保存/恢复(例如:您只是更改了globalAlpha),并使用约定/规则将上下文状态更改减少到最小。

最新更新