Starfield canvas程序占用了太多的CPU

时间:2016-05-19 05:31:25

标签: javascript html5 canvas cpu-usage

我使用canvas创建了一个星空,它按预期工作:

<!DOCTYPE HTML5>

<html>
    <head>
        <title>StarField</title>
        <style>
            * {
                margin: 0;
                padding: 0;
            }
            body {
                width: 100%;
                height: 100%;
            }
        </style>
    </head>

    <body onLoad="startGame()"></body>

    <script type="text/javascript">
        var NO_OF_STARS = 512;
        var stars = [];

        function startGame() {          
            gameArea.start(); /* Makes the canvas */

            gameRun = requestAnimationFrame(gameArea.update); /* Starts the game and coordinates all the animations */

            window.addEventListener("keydown", function(e) {
                if(e.keyCode == 27) { /* ESC stops everything */
                    stopEverything();
                }
            });
        }

        var gameArea = {
            canvas : document.createElement("canvas"),
            start : function() {
                document.body.appendChild(this.canvas);

                this.canvas.width = document.body.clientWidth;
                this.canvas.height = document.body.clientHeight;
            },
            update : function() {               
                gameArea.clear(); /* Fills the canvas with #000000 */
                gameArea.drawStars(); /* Draws the stars */

                gameRun = requestAnimationFrame(gameArea.update); /* Repeat the whole thing */
            },
            drawStars : function() {
                var ctx = gameArea.canvas.getContext("2d");

                if(stars.length == 0) {
                    for(var i = 0; i < NO_OF_STARS; i++) {
                        var opacity = ((Math.floor(Math.random() * 10) / 10) + .1);

                        stars.push([getRandomInt(0, gameArea.canvas.width - 1), getRandomInt(0, gameArea.canvas.height - 1),opacity]);

                        ctx.beginPath();
                        ctx.strokeStyle = "rgba(255, 255, 255, " + opacity + ")";
                        ctx.moveTo(stars[i][0], stars[i][1]);
                        ctx.lineTo(stars[i][0] + 1, stars[i][1] + 1);
                        ctx.stroke();
                    }
                } else {
                    for(var i = 0; i < NO_OF_STARS; i++) {
                        ctx.strokeStyle = "rgba(255, 255, 255, " + stars[i][2] + ")";

                        stars[i][0] -= ((stars[i][2] == 1.0) ? 5 :
                                        (stars[i][2] >= 0.8) ? 4 :
                                        (stars[i][2] >= 0.5) ? 3 :
                                        (stars[i][2] >= 0.3) ? 2 :
                                                               1);

                        if(stars[i][0] < 0) {
                            var opacity = ((Math.floor(Math.random() * 10) / 10) + .1);
                            stars.splice(i, 1, [gameArea.canvas.width, getRandomInt(0, gameArea.canvas.height - 1), opacity]);
                        }

                        ctx.beginPath();
                        ctx.moveTo(stars[i][0], stars[i][1]);
                        ctx.lineTo(stars[i][0] + 1, stars[i][1] + 1);
                        ctx.stroke();
                    }
                }
            },
            clear : function() {
                var ctx = this.canvas.getContext("2d");
                ctx.fillStyle = "#000000";
                ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
            }
        };

        /**
         * Returns a random integer between min (inclusive) and max (inclusive)
         */
        function getRandomInt(min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }

        function stopEverything() {
            cancelAnimationFrame(gameRun);
        }

    </script>

</html>

问题在于它需要占用大量CPU (在配备AMD A8四核处理器的笔记本电脑上占60%到65%)。我希望这个canvas程序也可以在其他具有低端处理器的计算机上运行。

我尝试过减少NO_OF_STARS,但这并没有改变CPU使用率。然而,当增加它时,动画会大大减慢并且CPU使用率会降低(我不会认为我会增加它,所以这并不是真的相关)

我还注意到画布的大小在CPU使用中起着重要作用。 (我上面提到的笔记本电脑的分辨率为1366x768)但我希望画布能够拍摄完整的视口。

那么,如何降低CPU使用率?

1 个答案:

答案 0 :(得分:2)

为每个星定义路径,描边样式并对其进行栅格化非常昂贵。尝试收集一些操作来减轻负担 - 这都是为了妥协:

  • 选择3-5个预定义的不透明度级别
  • 绘制星数除以预定义的不透明度水平数
  • 在循环
  • 之前使用单个beginPath()
  • 使用rect()代替moveTo + LineTo
  • 循环结束后
  • fill()一次,继续下一个不透明度级别
  • 获取3D上下文,而非每次调用
  • 对星形位置使用整数值(强制执行整数步骤,在这种情况下可能不理想,但值得一试)
  • 回收/重复使用星条目,而不是拼接和创建新条目
  • 减少计算次数和条件
  • 将帧速率降低到30 FPS(每隔一次切换RAF以进行绘制)。 60 FPS很不错,但是我们也可以像电影那样获得30 FPS(但是,它们会受到运动模糊的影响;我们可以通过使用带有嵌入式&#34;运动模糊&#34;而不是绘制矩形的精灵来作弊
  • 可选:将每个字段图层存储为单独的画布,以图像形式绘制(更快,但需要更多内存)。可以通过平铺旋转和/或翻转的画布来进行变化。

我个人会硬编码不透明度级别,但我将调整后的代码留下了随机生成(按下运行按钮几次)。

通常你会在CPU / GPU上受到一些打击,但这些提示应该可以提高性能(或者在这种情况下可以降低负载)。

&#13;
&#13;
var NO_OF_STARS = 500; // divisable by 5 (due to num. of opacities - see below)
var stars = [];

function startGame() {
  gameArea.start(); /* Makes the canvas */

  gameRun = requestAnimationFrame(gameArea.update); /* Starts the game and coordinates all the animations */

  window.addEventListener("keydown", function(e) {
    if (e.keyCode == 27) { /* ESC stops everything */
      stopEverything();
    }
  });
}

var gameArea = {
  canvas: document.createElement("canvas"),
  ctx: null,
  opacities: [],
  start: function() {
    document.body.appendChild(this.canvas);

    this.canvas.width = document.body.clientWidth;
    this.canvas.height = document.body.clientHeight;

    // store context once
    this.ctx = this.canvas.getContext("2d");
    
    // opacity levels
    for (var t = 0; t < 5; t++) this.opacities.push(((Math.floor(Math.random() * 10) / 10) + .1));

  },
  update: function() {
    gameArea.clear(); /* Fills the canvas with #000000 */
    gameArea.drawStars(); /* Draws the stars */

    gameRun = requestAnimationFrame(gameArea.update); /* Repeat the whole thing */
  },

  drawStars: function() {
    var ctx = this.ctx;

    if (!stars.length) {
      for (var i = 0; i < NO_OF_STARS; i++) {
        stars.push({
          x: getRandomInt(0, gameArea.canvas.width - 1)|0, 
          y: getRandomInt(0, gameArea.canvas.height - 1)|0
        });
      }
    }

    for (t = 0, pos = 0; t < 5; t++) {
      var opacity = this.opacities[t];
      ctx.beginPath();

      for (var i = 0; i < NO_OF_STARS / 5; i++) {
        
        stars[pos].x -= opacity * opacity * 4;

        if (stars[pos].x < 0) {
          stars[pos].x = gameArea.canvas.width;
          stars[pos].y = getRandomInt(0, gameArea.canvas.height - 1)|0;
        }

        ctx.rect(stars[pos].x, stars[pos].y, 1, 1);
        pos++;  // total position
      }

      ctx.strokeStyle = "rgba(255, 255, 255, " + opacity + ")";
      ctx.stroke();
    }
  },
  clear: function() {
    var ctx = this.canvas.getContext("2d");
    ctx.fillStyle = "#000000";
    ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
  }
};

/**
 * Returns a random integer between min (inclusive) and max (inclusive)
 */
function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function stopEverything() {
  cancelAnimationFrame(gameRun);
}

startGame();
&#13;
* {
  margin: 0;
  padding: 0;
}
body {
  width: 100%;
  height: 100%;
}
&#13;
&#13;
&#13;