0

I made a pretty basic demo, you press an arrow key and the square moves in that direction. One problem: when I first press the key, the square moves a little, pauses, then continues moving. How do I get rid of or work around the pause?

Code:

    var canvas = document.getElementById("canvas")
    var ctx = canvas.getContext('2d');
    var p1 = document.getElementById("p1");
    var keys = [];
    var x = 25
    var y = 25
    
    document.addEventListener("keydown", function(e) {
      keys[e.keyCode] = true;
      update();
    });
    
    document.addEventListener("keyup", function(e) {
      keys[e.keyCode] = false;
      update();
    });
    
    function update() {
      ctx.clearRect(0, 0, 400, 400)
      
      if(keys[40] == true) {
        y += 5
      }
      
      if(keys[38] == true) {
        y -= 5
      }
      
      if(keys[39] == true) {
        x += 5
      }
      
      if(keys[37] == true) {
        x -= 5
      }
      
      ctx.fillRect(x, y, 100, 100)
      
      console.log(keys);
    
      p1.innerText = "";
    
      for (i = 0; i < keys.length; i++) {
        if (keys[i]) {
          p1.innerText += i + " | ";
        }
      }
    }
    <canvas id='canvas' width='400' height='400'></canvas>
    <p id='p1'>testing</p>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
ARCS2016
  • 25
  • 4

1 Answers1

1

This happens because the keydown event is continuously fired at different intervals in different browsers.

Instead of relying on the browser to send keydown from time to time, it's better to have your own update loop running at 60fps, using requestAnimationFrame and each frame moving the box based on the keys pressed.

var canvas = document.getElementById("canvas")
var ctx = canvas.getContext('2d');
var p1 = document.getElementById("p1");
var keys = [];
var x = 25
var y = 25

document.addEventListener("keydown", function(e) {  
  e.preventDefault(); // make sure this doesn't scroll the window
  keys[e.keyCode] = true;
});

document.addEventListener("keyup", function(e) {
  keys[e.keyCode] = false;
});

function update() {
  //  Tell the browser to run again update when it is "free",
  // preferably at 60fps (actually your monitor's refresh rate)
  requestAnimationFrame(update);

  ctx.clearRect(0, 0, 400, 400)

  if(keys[40] == true) {
    y += 5
  }

  if(keys[38] == true) {
    y -= 5
  }

  if(keys[39] == true) {
    x += 5
  }

  if(keys[37] == true) {
    x -= 5
  }

  ctx.fillRect(x, y, 100, 100)

  p1.innerText = "";

  for (i = 0; i < keys.length; i++) {
    if (keys[i]) {
      p1.innerText += i + " | ";
    }
  }
}
update(); // Start running the loop at 60fps
<canvas id='canvas' width='400' height='400'></canvas>
<p id='p1'>testing</p>

Note that the distance the box moves in a given amount of time depends on your framerate, so if the browser can't keep up at 60fps and only runs at 30fps, update will be only called half of the times, so the box will only move half of the distance it would otherwise move at 60fps. To learn more about animations and game update loops I recommend to read this fix your timestep article.

XCS
  • 27,244
  • 26
  • 101
  • 151
  • 1
    I also think `Event.preventDefault()` should be used in a browser environment - when using arrows. And a diagonal movement compensation should also be taken into account. – Roko C. Buljan Apr 16 '19 at 20:02
  • @RokoC.Buljan Thanks for the suggestions, I added it to my answer. – XCS Apr 16 '19 at 20:11
  • I tried this code, but for some reason, it doe not run in Codepen. Do I need to add anything to make it run? – ARCS2016 Apr 16 '19 at 20:12
  • @ARCS2016 If you press "Run code snippet" above you should see it working. – XCS Apr 16 '19 at 20:13
  • Yeah nvm it's working now. I don't know why it didn't before. Thanks! – ARCS2016 Apr 16 '19 at 20:14