简体   繁体   中英

How can I get keydown events only once each time the key is pressed, even if it's held down, with JavaScript?

I am making a frogger replica and I want the frog to move only once when I press a key, basically to prevent it from moving multiple times if a key is held down.

This is the relevant part of my code that handles the keydown event:

document.onkeydown = function(e) {
  var key = e.which || e.keyCode;

  if (key == 37){ frog.x = frog.x - 50; }
  if (key == 38){ frog.y = frog.y - 50; }
  if (key == 39){ frog.x = frog.x + 50; }
  if (key == 40){ frog.y = frog.y + 50; }
};

Update:

I got it to not move when holding down keys, but now it won't let me move right after I moved right once, but will reset if I click another button, then does the same thing again:

 const canvas = document.getElementById('canvas'); const c = canvas.getContext('2d'); canvas.height = window.innerHeight; canvas.width = window.innerWidth; let frog = { x: 0, y: 0, fw: 50, fh: 50, fmx: 0, fmy: 0, }; let counter = 0; function animate() { requestAnimationFrame(animate); // Clear previous scene: c.clearRect(0, 0, window.innerWidth, window.innerHeight); // Draw frog: c.fillStyle = '#000' c.fillRect(frog.x, frog.y, frog.fw, frog.fh); // Movement of the frog with keys: document.onkeydown = function(e) { e = e || window.event; var key = e.which || e.keyCode; if (key == 65 && counter === 0) { frog.x = frog.x - 50, counter = 1 } if (key == 87 && counter === 0) { frog.y = frog.y - 50, counter = 1 } if (key == 68 && counter === 0) { frog.x = frog.x + 50, counter = 1 } if (key == 83 && counter === 0) { frog.y = frog.y + 50, counter = 1 } }; document.onkeyup = function(e) { e = e || window.event; var key = e.which || e.keyCode; if (key == 65) { counter = 0 } if (key == 87) { counter = 0 } if (key == 68) { coutner = 0 } if (key == 83) { counter = 0 } }; } animate();
 body { margin: 0; } #canvas { width: 100%; height: 100%; }
 <canvas id="canvas" />

You can use the KeyboardEvent.repeat property ( e.repeat ) to check if the user is just holding down the key and do nothing in that case.

Basically, your code should look like this:

if (e.repeat) return; // Do nothing

const key = e.which || e.keyCode;

if (key === 37) frog.x = frog.x - 50;
else if (key === 38) frog.y = frog.y - 50;
else if (key === 39) frog.x = frog.x + 50;
else if (key === 40) frog.y = frog.y + 50;

Also, KeyboardEvent.which and KeyboardEvent.keyCode are both deprecated, as stated in the docs:

Deprecated

This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.

You should be using KeyboardEvent.key instead:

 const frog = { x: 500, y: 500 }; document.addEventListener('keydown', (e) => { e.preventDefault(); if (e.repeat) return; // Do nothing const { key } = e; switch(key) { case 'ArrowUp': frog.y = frog.y - 50; break; case 'ArrowRight': frog.x = frog.x + 50; break; case 'ArrowDown': frog.y = frog.y + 50; break; case 'ArrowLeft': frog.x = frog.x - 50; break; } if (key.startsWith('Arrow')) console.log(frog); });
 .as-console-wrapper { max-height: 100%;important; }

Note that in your updated code you are not validating that the frog is inside the canvas and you are defining the onkeydown listener inside the rendering function. You should validate that and define the even listener only once, outside:

 const frog = { x: 0, y: 0, fw: 50, fh: 50, }; const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const MAX_X = window.innerWidth - frog.fw; const MAX_Y = window.innerHeight - frog.fh; canvas.width = window.innerWidth; canvas.height = window.innerHeight; function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#000'; ctx.fillRect(frog.x, frog.y, frog.fw, frog.fh); requestAnimationFrame(animate); } // This should be declared outside the rendering function: document.onkeydown = (e) => { e.preventDefault(); if (e.repeat) return; // Do nothing const { key } = e; switch(key) { case 'ArrowUp': frog.y = Math.max(0, frog.y - 50); break; case 'ArrowRight': frog.x = Math.min(frog.x + 50, MAX_X); break; case 'ArrowDown': frog.y = Math.min(frog.y + 50, MAX_Y); break; case 'ArrowLeft': frog.x = Math.max(0, frog.x - 50); break; } }; requestAnimationFrame(animate);
 body { margin: 0; } #canvas { width: 100%; height: 100%; }
 <canvas id="canvas" />

The keyboard driver will normally fire repeated keystrokes, which turn into distinct keypress events. Using this facility is generally not what you want for gaming, however, as the repeat delay is annoying, and both the repeat and initial repeat delay are OS-wide user preferences.

Instead, you should trap the keydown and keyup events. When the key goes down, use setInterval or setTimeout to trigger a reiteration timer and event. When the key goes up, cancel the timer.

My preference would be use setTimeout, in case you miss the keyup event due to something like the user minimizing his window with the mouse while holding down the key.

I did what the one guy said(great advice btw, i'm glad you told me),but when i press an arrow, the shape disappears.


canvas.height = window.innerHeight;
canvas.width = window.innerWidth;

//variables

let frog = {
    x: 500,
    y: 500,
    fw: 50,
    fh: 50,
    fmx: 0,
    fmy: 0
}

//running it all

function animate(){
    requestAnimationFrame(animate)
    c.clearRect(0,0,innerWidth,innerHeight)

    //frog
    c.fillStyle = '#000'
    c.fillRect(frog.x,frog.y,frog.fw,frog.fh)


document.addEventListener('keydown', (e) => {
  e.preventDefault();

  if (e.repeat) return; // Do nothing

  const { key } = e;

  switch(key) {
    case 'ArrowUp':
      frog.y = frog.y - 50;
      break;

    case 'ArrowRight':
      frog.x = frog.x + 50;
      break;

    case 'ArrowDown':
      frog.y = frog.y + 50;
      break;

    case 'ArrowLeft':
      frog.x = frog.x - 50;
      break;  
  }
  if (key.startsWith('Arrow')) console.log(frog);
});

}

animate();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM