【问题标题】:Why does my canvas decrease in performance after a while?为什么我的画布在一段时间后性能下降?
【发布时间】:2018-11-30 06:01:24
【问题描述】:

我有以下代码,当你按下向上箭头键时,播放器会跳转(对不起,它很长):

class Vec {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }
}

class Rect {
  constructor(w, h) {
    this.pos = new Vec;
    this.size = new Vec(w, h);
    this.vel = new Vec;
    this.last = new Vec;
  }
}

class Player extends Rect {
  constructor() {
    super(40, 40);
  }
}

// setup
const backgroundContext = document.getElementById("backgroundCanvas").getContext("2d");
const groundContext = document.getElementById("groundCanvas").getContext("2d");
const objectContext = document.getElementById("objectCanvas").getContext("2d");

const WIDTH = 600;
const HEIGHT = 400;
const GROUND_Y = 50;

const player = new Player;
player.pos.x = 100;
player.pos.y = 390;
player.last.x = player.pos.x;
player.last.y = player.pos.y;
player.vel.x = 0;
let isJumping = true;
const JUMP_STRENGTH = -300;
const GRAVITY = 10;

function update(dt) {
  // update player
  player.last.x = player.pos.x;
  player.last.y = player.pos.y;

  player.vel.y += GRAVITY;
  player.pos.y += player.vel.y * dt;
  player.pos.y = Math.round(player.pos.y);

  document.addEventListener("keydown", (e) => {
    if (e.keyCode === 38 && isJumping === false) {
      isJumping = true;
      player.vel.y = JUMP_STRENGTH;
    }
  }, false);
  if (player.pos.y > HEIGHT - GROUND_Y - player.size.y) {
    isJumping = false;
    player.pos.y = HEIGHT - GROUND_Y - player.size.y;
    player.vel.y = 0;
  }
}

function draw() {
  // draw background
  backgroundContext.fillStyle = "#000";
  backgroundContext.fillRect(0, 0, WIDTH, HEIGHT);

  // draw ground
  objectContext.clearRect(0, HEIGHT - GROUND_Y, WIDTH, GROUND_Y);
  groundContext.fillStyle = "#00ff00";
  groundContext.fillRect(0, HEIGHT - GROUND_Y, WIDTH, GROUND_Y);

  // draw player
  objectContext.clearRect(player.last.x, player.last.y, player.size.x, player.size.y);
  objectContext.fillStyle = "#fff";
  objectContext.fillRect(player.pos.x, player.pos.y, player.size.x, player.size.y);
}

// game loop
const TIMESTEP = 1 / 60;
let accumulator = 0;
let lastRender = 0;

function loop(timestamp) {
  accumulator += (timestamp - lastRender) / 1000;
  lastRender = timestamp;
  while (accumulator >= TIMESTEP) {
    update(TIMESTEP);
    draw();
    accumulator -= TIMESTEP;
  }

  requestAnimationFrame(loop);
}

requestAnimationFrame(loop);
canvas {
  position: absolute;
  left: 0;
  top: 0;
}
<!DOCTYPE html>
<html>

<head>
  <title>Jump_Over_It</title>
  <link href="css/default.css" rel="stylesheet" />
</head>

<body>
  <canvas style="z-index: 0;" id="backgroundCanvas" width="600" height="400"></canvas>
  <canvas style="z-index: 1;" id="groundCanvas" width="600" height="400"></canvas>
  <canvas style="z-index: 2;" id="objectCanvas" width="600" height="400"></canvas>
  <script src="js/main.js"></script>
</body>

</html>

我有三个问题:

1) 如果您等待大约一分钟,您会开始注意到性能开始下降。为什么会这样?

2) 为什么按住向上键时性能会明显下降?

3) 在游戏循环中,我做到了:

while (accumulator >= TIMESTEP) {
  update(TIMESTEP);
  draw();
  accumulator -= TIMESTEP;
}

可以将draw() 函数与update() 函数放在同一个while 循环中吗?

如果你知道该怎么做,请告诉我。

【问题讨论】:

    标签: javascript html5-canvas


    【解决方案1】:

    对于问题 1 和 2:

    这是因为您在 while 循环中添加了一个新的 EventListener,它本身在 requestAnimationFrame 循环中。

    我什至不会计算附加的事件处理程序的数量,也不会运行这个 sn-p,但不要触摸你的键盘,因为那里可能有无数个处理程序在串行执行。

    要正确解决此问题,请将您的 addEventListener 调用移出这些循环,您只需调用一次。

    对于问题 3:

    不清楚为什么你甚至需要这个 while 循环。直接实际计算新位置而不是像这样循环更新会更好。
    但至少,没有。你不应该在这个 while 循环中调用draw,因为每次调用都会否定之前的调用。所以在你的 rAF 处理程序结束时只调用一次。

    另外,请注意,与其清除对象所在的部分,不如每次都清除整个画布,甚至可以考虑将所有绘图移动到一个画布上,因为您认为合成是当在屏幕上绘制 3 个 DOM 元素时,实际上仍然会发生三个画布,从而赢得胜利。

    class Vec {
      constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
      }
    }
    
    class Rect {
      constructor(w, h) {
        this.pos = new Vec;
        this.size = new Vec(w, h);
        this.vel = new Vec;
        this.last = new Vec;
      }
    }
    
    class Player extends Rect {
      constructor() {
        super(40, 40);
      }
    }
    
    // setup
    const backgroundContext = document.getElementById("backgroundCanvas").getContext("2d");
    const groundContext = document.getElementById("groundCanvas").getContext("2d");
    const objectContext = document.getElementById("objectCanvas").getContext("2d");
    
    const WIDTH = 600;
    const HEIGHT = 400;
    const GROUND_Y = 50;
    
    const player = new Player;
    player.pos.x = 100;
    player.pos.y = 390;
    player.last.x = player.pos.x;
    player.last.y = player.pos.y;
    player.vel.x = 0;
    let isJumping = true;
    const JUMP_STRENGTH = -300;
    const GRAVITY = 10;
    
    function update(dt) {
      // update player
      player.last.x = player.pos.x;
      player.last.y = player.pos.y;
    
      player.vel.y += GRAVITY;
      player.pos.y += player.vel.y * dt;
      player.pos.y = Math.round(player.pos.y);
    
      if (player.pos.y > HEIGHT - GROUND_Y - player.size.y) {
        isJumping = false;
        player.pos.y = HEIGHT - GROUND_Y - player.size.y;
        player.vel.y = 0;
      }
    }
    document.addEventListener("keydown", (e) => {
      if (e.keyCode === 38 && isJumping === false) {
        e.preventDefault();
        isJumping = true;
        player.vel.y = JUMP_STRENGTH;
      }
    }, false);
    
    function draw() {
      // draw background
      backgroundContext.fillStyle = "#000";
      backgroundContext.fillRect(0, 0, WIDTH, HEIGHT);
    
      // draw ground
      groundContext.clearRect(0, 0, WIDTH, HEIGHT);
      groundContext.fillStyle = "#00ff00";
      groundContext.fillRect(0, HEIGHT - GROUND_Y, WIDTH, GROUND_Y);
    
      // draw player
      objectContext.clearRect(0, 0, WIDTH, HEIGHT);  objectContext.fillStyle = "#fff";
      objectContext.fillRect(player.pos.x, player.pos.y, player.size.x, player.size.y);
    }
    
    // game loop
    const TIMESTEP = 1 / 60;
    let accumulator = 0;
    let lastRender = 0;
    
    function loop(timestamp) {
      accumulator += (timestamp - lastRender) / 1000;
      lastRender = timestamp;
      while (accumulator >= TIMESTEP) {
        accumulator -= TIMESTEP;
        update(TIMESTEP);
      }
      draw();
    
      requestAnimationFrame(loop);
    }
    
    requestAnimationFrame(loop);
    canvas {
      position: absolute;
      left: 0;
      top: 0;
    }
    <!DOCTYPE html>
    <html>
    
    <head>
      <title>Jump_Over_It</title>
      <link href="css/default.css" rel="stylesheet" />
    </head>
    
    <body>
      <canvas style="z-index: 0;" id="backgroundCanvas" width="600" height="400"></canvas>
      <canvas style="z-index: 1;" id="groundCanvas" width="600" height="400"></canvas>
      <canvas style="z-index: 2;" id="objectCanvas" width="600" height="400"></canvas>
      <script src="js/main.js"></script>
    </body>
    
    </html>

    【讨论】:

      猜你喜欢
      • 2015-04-10
      • 1970-01-01
      • 1970-01-01
      • 2015-09-27
      • 2019-12-22
      • 1970-01-01
      • 2013-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多