【问题标题】:How can I get keydown events only once each time the key is pressed, even if it's held down, with JavaScript?如何在每次按下键时仅获取一次 keydown 事件,即使它被按住,使用 JavaScript?
【发布时间】:2020-08-15 08:03:07
【问题描述】:

我正在制作一个青蛙复制品,我希望青蛙在我按下一个键时只移动一次,基本上是为了防止它在按住一个键时移动多次。

这是我的代码中处理keydown 事件的相关部分:

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; }
};

更新:

我让它在按住键时不移动,但现在它不会让我在我向右移动一次后立即移动,但如果我单击另一个按钮会重置,然后再次执行相同的操作:

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" />

【问题讨论】:

标签: javascript event-handling html5-canvas dom-events keyboard-events


【解决方案1】:

您可以使用KeyboardEvent.repeat 属性 (e.repeat) 来检查用户是否只是按住键而在这种情况下什么都不做。

基本上,您的代码应如下所示:

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;

此外,KeyboardEvent.whichKeyboardEvent.keyCode 均已弃用,如文档中所述:

已弃用

不再推荐使用此功能。尽管某些浏览器可能仍然支持它,但它可能已经从相关的 Web 标准中删除,可能正在被删除,或者可能仅出于兼容性目的而保留。避免使用它,并尽可能更新现有代码;请参阅本页底部的兼容性表以指导您的决定。请注意,此功能可能随时停止工作。

您应该改用KeyboardEvent.key

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;
}

请注意,在您更新的代码中,您并未验证青蛙是否在画布内,而是在渲染函数中定义 onkeydown 侦听器。您应该验证这一点并在外部只定义一次偶数侦听器:

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" />

【讨论】:

  • 酷,这是一个很大的帮助,但还有另一个问题。当我运行它并单击箭头时,形状消失了。
  • 嗯,这是一个完全不同的问题。可能50 的增量太多,或者您的渲染逻辑可能存在完全不同的问题。此外,您应该确保青蛙不会超出屏幕的可见部分。
  • @DrummerGaines 检查更新的示例。 onkeydown 监听器应该在渲染函数之外定义。
  • 非常感谢。这是一个很大的帮助。但是当我按住键时,它移动一次,然后移动两次,然后停止。我知道如何让它只移动一次。谢谢
  • 嗯,每次你按下一个键它只会移动一次,但如果你按住它就不会移动多次。它不应该是这样工作的吗?
【解决方案2】:

键盘驱动程序通常会触发重复的击键,这会变成不同的按键事件。但是,使用此功能通常不是您想要的游戏,因为重复延迟很烦人,而且重复和初始重复延迟都是操作系统范围内的用户偏好。

相反,您应该捕获 keydown 和 keyup 事件。当按键按下时,使用 setInterval 或 setTimeout 触发重复计时器和事件。当按键上升时,取消计时器。

我的偏好是使用 setTimeout,以防您因为用户在按住键时用鼠标最小化窗口等原因而错过了 keyup 事件。

【讨论】:

    【解决方案3】:

    我按照那个人说的做了(顺便说一句很好的建议,我很高兴你告诉我),但是当我按下箭头时,形状消失了。

    
    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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多