为什么 onkeyup 事件在 Javascript 游戏中没有触发

问题描述 投票:0回答:2

我开始了 2d Javascript 游戏 - 目前玩家可以使用箭头键在屏幕上驱动一个三角形。

问题是,有时三角形会卡在向一个方向旋转或向前移动,直到再次按下相应的控制键并再次触发

onkeyup
事件。当同时按下多个控制键时,通常会发生这种情况。

我无法弄清楚为什么它首先会卡住,除非

onkeyup
事件由于某种原因没有被解雇。任何帮助将不胜感激,谢谢。

这是一些代码,您可以在 JSFiddle 上找到完全有效的示例

...

function init(){
    var canvas = document.getElementById('canvas');
    if(canvas.getContext){
        setInterval(play, 50);
    }
}

function play(){
    printTriState();
    updateTriAcceleration();
    applyTriVelocity();
    updateTriRotation();
    draw();
}

document.onkeydown = function(event){

if(!event.keyCode){
    keycode = window.event.keyCode;
} else {
    keycode = event.keyCode;
}
console.log(event.keyCode);
switch(keycode){
    //left
    case 37:
    tri.rotation = -1;
    break;

    //up
    case 38:
    tri.throttle = true;
    break;

    //right
    case 39:
    tri.rotation = 1;
    break;

    //down
    case 40:
    tri.currentSpeed = 0;
    break;

    default:
    break;
}
};

document.onkeyup = function(event){

if(!event.keyCode){
    keycode = window.event.keyCode;
} else {
    keycode = event.keyCode;
}
console.log(event.keyCode);
switch(keycode){
    //left
    case 37:
    tri.rotation = 0;
    break;

    //up
    case 38:
    tri.throttle = false;
    break;

    //right
    case 39:
    tri.rotation = 0;
    break;

    //down
    case 40:

    break;

    default:
    break;
}
};

function updateTriRotation(){
    if(tri.rotation == 1){
        tri.orientation += tri.rotationSpeed;
    } else if (tri.rotation == -1){
        tri.orientation -= tri.rotationSpeed;
    } else {
        tri.orientation += 0;
    }
}

...
javascript dom-events onkeydown onkeyup
2个回答
15
投票

问题描述

keyup
事件 会被触发,但有时最终的
keydown
事件会在
keyup
之后直接触发。诡异的?是的。

问题在于如何实现按住键的

keydown
事件重复的实现方式:最后一个按住的键的
keydown
 事件被重复触发。由于优化的 JavaScript 代码执行的异步、单线程特性,有时可能会在调用相应的 
keydown
 事件之后
触发此类重复的 keyup 事件。
看看当我同时按住并释放 [UP] 和 [RIGHT] 键时会发生什么

在我的 JSFiddle 示例中

在 Windows 7 上使用 Firefox 13.0.1 观察到

rotation: 1 key down: 39 rotation: 1 key down: 38 rotation: 1 key down: 38 rotation: 1 key down: 38 rotation: 1 key up: 38 rotation: 0 key up: 39 rotation: 1 key down: 38

您可以在此示例控制台输出中看到三件事:

仅重复触发 [RIGHT] (38) 的
    keydown
  1. 事件。
    当我释放按键时,[RIGHT] (38) 
  2.  事件被触发。
    最后一个预定的 keyup
  3. 事件被触发,
  4. after
     
    keydown 被执行。
    这就是为什么即使没有按下任何键,您的
  5. keyup
状态也会“卡住”值

rotation

解决方案


为了避免这种情况,您可以跟踪释放按键时的微秒数。当按键事件发生时

仅几微秒

,然后我们默默地忽略它。

我在您的代码中引入了一个辅助对象 1

在这个 JSFiddle 示例中查看它的工作原理。


现在控制台输出如下所示:

Key

我的解决方案受到
这篇博客文章
的启发,该文章更进一步,解释了如何避免在使用 JavaScript 按键事件进行游戏开发时遇到的草率和“仅限单向”问题。这绝对值得一读。

忽略延迟的 rotation: 1 key down: 40 time: 1340805132040 rotation: 1 key down: 40 time: 1340805132071 rotation: 1 key down: 40 time: 1340805132109 rotation: 1 key up: 40 time: 1340805132138 rotation: 0 key up: 39 time: 1340805132153 key down: 40 release time: 1340804109335 keydown fired after keyup .. nothing to do. rotation: 0 key down: 39 time: 1340805132191

事件的解决方法基本上如下所示:

keydown

更新 #1

在 JSFiddle 上查找代码的更新(固定)版本

这个问题已经有近 8 年历史了,但我遇到了类似的问题并偶然发现了这个页面,所以万一它对其他人有帮助:

7
投票
我的代码中遇到了干扰问题,即在按下键盘时使用

var Key = { _pressed: {}, _release_time: {}, MAX_KEY_DELAY: 100, onKeydown: function(event) { // avoid firing of the keydown event when a keyup occured // just +/- 100ms before or after a the keydown event. // due to the asynchronous nature of JS it may happen that the // timestamp of keydown is before the timestamp of keyup, but // the actual execution of the keydown event happens *after* // the keyup. Weird? Yes. Confusing! var time = new Date().getTime(); if ( this._release_time[event.keyCode] && time < this._release_time[event.keyCode] + this.MAX_KEY_DELAY ) { console.log('keydown fired after keyup .. nothing to do.'); return false; } this._pressed[event.keyCode] = true; // LOGIC FOR ACTUAL KEY CODE HANDLING GOES HERE }, onKeyup: function(event) { delete this._pressed[event.keyCode]; this._release_time[event.keyCode] = new Date().getTime(); // LOGIC FOR ACTUAL KEY CODE HANDLING GOES HERE } }; document.onkeydown = function(event) { return Key.onKeydown(event); }; document.onkeyup = function(event) { return Key.onKeyup(event); };

Set

 键盘事件,并在按下键盘时使用 
add
。我觉得使用 
delete
 字段来区分按键(而不是使用 
event.key
)是一种很好的做法,以维护用户键盘上写入的内容与我的代码解释的内容之间的语义映射。 (例如,我相信 QWERTY 键盘的 W 会报告与 AZERTY 键盘的 Z 相同的 
keyCode
,但没有法语键盘可供验证。)
这工作得很好(我没有看到其他人报告的无序事件的问题),直到修改器出现为止。我发现了两个问题:

...举行 Shift 的地方。如果用户的击键顺序为(hold-shift、hold-R、release-shift、release-R),则 JS 事件的顺序为 wrt。

keyCode
    将会运行(keydown-shift、keydown-R、keyup-shift、keyup-r)。换句话说,按下了
  • event.key
    ,但是释放了 
    R
    ,所以我的 
    r
    Set
     被卡住了。我的解决方案是将 
    R
     转换为 
    Set
    ,其中键是 
    Map
    ,值是在 keydown 事件时报告的 
    event.keyCode
    。这样,当我收到 keyup 事件时,我只需 
    event.key
     对应于 
    delete
     的地图键,并且按键不会被卡住。我希望选项/替代项需要相同的处理。
    ...Meta 键所在的位置(Mac 上的 Command;我猜 Windows 上的 Control?)。在这种情况下,执行的序列(hold-meta、hold-K、release-meta、release-K)将生成事件(keydown-meta、keydown-k、keyup-meta)。因此,K 的 keyup 事件确实丢失了,即使我确信会做 
    keyCode
  • 来防止系统窃取我的事件。我希望我有一个更好的解决方案,但我现在正在做的是当我看到元关键事件时对整个地图进行核攻击。我应该郑重声明一下,我的代码实际上并没有使用 Meta 作为修饰符;我只是想确保它是安全的,当用户快速打字时,我的按键处理程序似乎很容易被卡住。
  • 
    
    哦,还有,关于模糊——我正在用核武器攻击地图。那么所有的赌注都结束了...

© www.soinside.com 2019 - 2024. All rights reserved.