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

Javascript 为什么这个画布代码这么慢?,javascript,canvas,webgl,Javascript,Canvas,Webgl,我有一个简短的代码,在这些轨道上画圆(轨道)点(卫星)。卫星正在绕轨道运行。事实上,代码不是我的,但我被要求解决这个问题 根据chrome和firefox中的profiler,functiondrawSatellite消耗了50%-100%的cpu,我想知道原因 画布与您的窗口一样大(1920x1080)。大约有160个轨道(随着page上线时间的增加而增加) 这是绘图卫星: OrbitBackground.prototype.drawSatellite = function(ctx, sate

我有一个简短的代码,在这些轨道上画圆(轨道)点(卫星)。卫星正在绕轨道运行。事实上,代码不是我的,但我被要求解决这个问题

根据chrome和firefox中的profiler,function
drawSatellite
消耗了50%-100%的cpu,我想知道原因

画布与您的窗口一样大(1920x1080)。大约有160个轨道(随着page上线时间的增加而增加)

这是
绘图卫星

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]);
  }
};
OrbitBackground.prototype.drawFrame=函数(){
如果(这个正在运行)
requestAnimationFrame(this.drawFrame.bind(this));
this.dynamicStageCtx.clearRect(0,0,this.pageWidth,this.pageHeight);
对于(var i=0;i
您正在这样做:

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)
这样做会更快:

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缩放。

你这样做:

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)
这样做会更快:

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缩放它。

<强>可能是个问题:>/P> 在您的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});
}

可能有问题:

在您的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});
}

我认为问题可能是,我们使用画布作为2D上下文,使用javascript函数而不是着色器,所以操作可能太繁重,因为cpu无法为每个帧调用所有这些javascript函数?但这只是一个理论…你的FPS太低了吗?高CPU消耗并不总是意味着代码速度慢或不是最优的。你需要注意的是,你的draw方法的运行速度比(1000/targetFPS)要快。msIn chrome fps还可以,在firefox中fps非常低,有时浏览器会死机。在嵌入式系统上,fps是超低的。1)缓存:satellite.satellite可以缓存以避免6次访问,satellite.orbit.radius也可以缓存。2) 填什么?规范化填充对性能有好处:创建填充后,执行ctx.fillStyle=myFill;然后myFill=ctx.fillStyle;所以fill的格式完全正确。3) 尝试使用fillRect以查看圆形绘制是否不是瓶颈。4) 请注意,FF和Ch都有画布调试/分析工具。嗯,我尝试了两个示例。至少在我的机器上,我认为问题可能是,我们使用画布作为2D上下文,使用javascript函数而不是着色器,所以操作可能太繁重,因为cpu无法为每个帧调用所有这些javascript函数?但这只是一个理论…你的FPS太低了吗?高CPU消耗并不总是意味着代码速度慢或不是最优的。你需要注意的是,你的draw方法的运行速度比(1000/targetFPS)要快。msIn chrome fps还可以,在firefox中fps非常低,有时浏览器会死机。在嵌入式系统上,fps是超低的。1)缓存:satellite.satellite可以缓存以避免6次访问,satellite.orbit.radius也可以缓存。2) 填什么?规范化填充对性能有好处:创建填充后,执行ctx.fillStyle=myFill;然后myFill=ctx.fillStyle;所以fill的格式完全正确。3) 尝试使用fillRect以查看圆形绘制是否不是瓶颈。4) 请注意,FF和Ch都有画布调试/分析工具。嗯,我尝试了两个示例。至少在我的机器上,谢谢你的建议。画布不能更小,因为它应该是页面的背景,所以它取决于客户端屏幕。事实上,我的颜色很少,所以我会尝试将它们组合在一起。画布可以更小,然后用CSS放大到其大小的两倍或三倍,但这需要fidelityThx的损失作为好的建议。画布不能更小,因为它应该是页面的背景,所以它取决于客户端屏幕。事实上,我没有多少颜色,所以我会尝试将它们组合在一起。画布可以更小,然后用CSS放大到其大小的两倍或三倍,但这会损失逼真度