为什么这个画布代码这么慢?

时间:2014-05-02 12:06:32

标签: javascript canvas webgl

我有短代码,在这些轨道上绘制圆(轨道)点(卫星)。卫星在轨道上移动。事实上代码并不是我的,但我被要求解决问题。

根据chrome和firefox中的分析器,函数drawSatellite吃了50%-100%的cpu,我想知道原因。

Canvas与您的窗口相同(1920x1080)。有大约160个轨道(随着时间页增加在线)。

这是drawSatellite

OrbitBackground.prototype.drawSatellite = function(ctx, satellite) {
  ctx.fillStyle = satellite.satellite.fill;
  ctx.beginPath();

  if (++satellite.satellite.angularPosition == 360)
    satellite.satellite.angularPosition = 0;

  // 1 FPS = 60 calls => 180 / 6 (6-times faster @ 60 FPS) = 30
  var radians = satellite.satellite.angularPosition * Math.PI / 30 / satellite.rps;

  if (satellite.backward)
    radians = -radians;

  ctx.arc(
      satellite.satellite.x + satellite.orbit.radius * Math.cos(radians),
      satellite.satellite.y + satellite.orbit.radius * Math.sin(radians),
      satellite.satellite.radius,
      0,
      Math.PI*2,
      true
  );
  ctx.closePath();
  ctx.fill();
};

调用它的函数:

OrbitBackground.prototype.drawFrame = function() {
  if (this.running)
    requestAnimationFrame(this.drawFrame.bind(this));

  this.dynamicStageCtx.clearRect(0, 0, this.pageWidth, this.pageHeight);

  for (var i=0; i < this.orbits.length; i++) {
    this.drawSatellite(this.dynamicStageCtx, this.orbits[i]);
  }
};

2 个答案:

答案 0 :(得分:4)

你这样做:

Loop:
    set fill style
    begin path
    make path
    end path
    fill

这样做会更快:

set fill style (just once, before loop)
begin path (just one path, with loop-number of subpaths)
Loop:
    moveTo (start of subpath)
    make path
    close path
fill (just once, after loop)

但这需要每个卫星上的填充样式相同。如果只有几种颜色,你可以尝试按颜色将它们捆在一起。

另请注意,计算余弦和正弦的速度很慢(所有三角函数和平方根调用都很慢),如果你可以避免使用它们,那么你会更好。

画布的大小(像素数)也很重要。考虑将画布制作为半尺寸或四分之一尺寸(960x540或480x270)并使用CSS进行缩放。

答案 1 :(得分:3)

可能有问题:

我在ctx.arc命令之前没有看到ctx.beginPath。

如果没有ctx.beginPath,您之前的所有弧都将与当前弧一起重绘。

小优化

将Math.PI * 2分配给变量,因为它经常使用

var PI2=Math.PI*2;

如何消除代码中最慢的部分(Math.cos和Math.sin)。

由于您的节点正在重复轨道运行,因此您可以预先计算所有未翻译的[x,y]以获得完整的轨道。

var statellite.orbitTrig=[];

for(var i=0;i<360;i++){
    var radians=PI2/360*i;
    var x=satellite.orbit.radius * Math.cos(radians)
    var y=satellite.orbit.radius * Math.sin(radians)
    satellite.orbitTrig.push({x:x,y:y});
}

然后您可以参考动画循环中的预先计算的值。

var trig=satellite.orbitTrig[satellite.satellite.angularPosition];
var x=satellite.satellite.x + trig.x;
var y=satellite.satellite.y + trig.y;
ctx.beginPath();
ctx.arc(x,y,satellite.satellite.radius,0,PI2);
ctx.closePath();
ctx.fill();