【问题标题】:Listen to multiple keydowns听多个按键
【发布时间】:2011-10-01 05:02:30
【问题描述】:

我试图让用户使用箭头键移动页面上的元素。到目前为止,我的运动适用于上/下/左/右,但不适用于对角线(同时按下两个箭头键)。

我的听众是这样的:

addEventListener('keydown', function(e){
    move = false;
    x = false;
    y = false;
    var keycode;
    if (window.event) keycode = window.event.keyCode;
    else if (e) keycode = e.which;
    switch(keycode){
        case 37:
            move = true;
            x = 'negative';
            //prevent page scroll
            e.preventDefault()
        break;
        case 38:
            move = true;
            y = 'negative'
            //prevent page scroll
            e.preventDefault()
        break;
        case 39:
            move = true;
            x = 'positive'
            //prevent page scroll
            e.preventDefault()
        break;
        case 40:
            move = true;
            y = 'positive'
            //prevent page scroll
            e.preventDefault()
        break;
    }
    if(move){
        animation.move(x,y);
    }
    return false;
})

这个想法是,如果用户按下箭头键,它会将 xy 设置为 negativepositive,并触发 move() 函数,该函数会将元素移动到预设所需方向上的像素数,并且如果按下两个键,则会触发第二个事件......我也希望能够让用户通过快速释放和按下键来无缝地改变方向但是这些都没有发生,如果用户按下另一个方向键,他们似乎需要等待片刻才能发生移动,除非他们完全释放该键然后再按下另一个键,并且它根本不会响应第二个键,直到第一个键是发布。

【问题讨论】:

    标签: javascript keydown addeventlistener


    【解决方案1】:

    小提琴:http://jsfiddle.net/ATUEx/

    创建一个临时缓存来记住您的击键。

    处理两个键的实现将遵循以下模式:

    1. <keydown>
      • 清除之前的超时。
      • 检查a键码是否被缓存。
        如果是,则组合有效:
        -删除所有缓存的键码
        -对该组合执行函数
        else
        -删除所有缓存的密钥代码
        -存储新密钥代码
        -设置清除密钥代码的超时时间(见下文),并有合理的延迟
    2. 重复 1

    合理的延迟:通过实验了解哪种超时对您来说足够。当延迟太短时,下一个发起的事件将找不到之前输入的键码。

    延迟时间过长,不想要的时候按键会叠加。


    代码

    我创建了一个高效的函数,牢记您的代码。您应该能够非常轻松地实现它。

    (function(){ //Anonymous function, no leaks
        /* Change the next variable if necessary */
        var timeout = 200; /* Timeout in milliseconds*/
    
        var lastKeyCode = -1;
        var timer = null;
        function keyCheck(ev){
            var keyCode = typeof ev.which != "undefined" ? ev.which : event.keyCode;
            /* An alternative way to check keyCodes:
             * if(keyCode >= 37 && keyCode <= 40) ..*/
             /*37=Left  38=Up  39=Right  40=Down */
            if([37, 38, 39, 40].indexOf(keyCode) != -1){
    
                /* lastKeyCode == -1 = no saved key
                   Difference betwene keyCodes == opposite keys = no possible combi*/
                if(lastKeyCode == -1 || Math.abs(lastKeyCode - keyCode) == 2){
                    refresh();
                    lastKeyCode = keyCode;
                } else if(lastKeyCode == keyCode){
                    clear([lastKeyCode]);
                } else {
                    /* lastKeyCode != -1 && keyCode != lastKeyCode
                       and no opposite key = possible combi*/
                    clear([lastKeyCode, keyCode]);
                    lastKeyCode = -1
                }
                ev.preventDefault(); //Stop default behaviour
                ev.stopPropagation(); //Other event listeners won't get the event
            }
    
            /* Functions used above, grouped together for code readability */
            function reset(){
                keyCombi([lastKeyCode]);
                lastKeyCode = -1;
            }
            function clear(array_keys){
                clearTimeout(timer);
                keyCombi(array_keys);
            }
            function refresh(){
                clearTimeout(timer);
                timer = setTimeout(reset, timeout);
            }
        }
    
        var lastX = false;
        var lastY = false;
        function keyCombi(/*Array*/ keys){
            /* Are the following keyCodes in array "keys"?*/
            var left = keys.indexOf(37) != -1;
            var up = keys.indexOf(38) != -1;
            var right = keys.indexOf(39) != -1;
            var down = keys.indexOf(40) != -1;
    
            /* What direction? */
            var x = left ? "negative" : right ? "positive" : false;
            var y = up ? "negative" : down ? "positive" : false;
            /* Are we heading to a different direction?*/
            if(lastX != x || lastY != y) animation.move(x, y);
            lastX = x;
            lastY = y;
        }
    
        //Add event listener
        var eventType = "keydown";window["on"+eventType] = keyCheck;
    })();
    

    在匿名函数的最后,添加了keydown事件监听器。此事件仅触发一次(按下键时)。当第二个按键被足够快地按下时,代码会识别出两个按键,并调用keyCombi()

    我将keyCombi 设计为智能的,并且仅在值更改时才调用animation.move(x,y)。另外,我实现了一次处理两个方向的可能性。

    注意:我已将函数包含在匿名函数包装器中,因此变量未定义在全局 (window) 范围内。如果您不关心范围,请随意删除第一行和最后一行。

    【讨论】:

    • 我很好奇你说我应该删除缓存的键码。我想我主要想在keyup 事件上这样做?还是只要按住键,keydown 事件就会一直触发?
    • @ChrisSobolewski 我已经更新了我的答案。另请查看 Fiddle 链接以获取实时示例。
    • 哇。谢谢!这和我要去的地方很相似……你肯定超越了先生。
    【解决方案2】:

    这是 您的 代码,稍作修改...http://jsfiddle.net/w8uNz/ 它可以正常工作。跨父元素移动元素。

    Rob W 是对的,您应该在一段时间内对按键进行动态缓存。但是为了什么?如果您正在制作动态游戏 - 必须有更高的抽象性。总的来说,如果你想做这样的事情,你需要提高你的编码水平。 (否则只会让你头疼。)

    【讨论】:

      【解决方案3】:

      从逻辑上讲,这听起来很简单:

      1. 第一个按键触发按键按下事件,将此事件存储在堆栈中
      2. 然后第二个按键触发按键按下事件,将此事件存储在堆栈中
      3. 现在你有两个按键,没有按键
      4. 如果按键启动,您有三种可能性
        1. 你只有一个事件 => 朝着那个方向前进
        2. 你有两个事件 => 对角线
        3. 您有两个以上的事件,或者一个无效的选项(例如,左右)=> 什么也不做,或者您喜欢的任何事情
      5. 清空堆栈

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-01
        • 2014-05-22
        • 1970-01-01
        • 1970-01-01
        • 2014-10-08
        • 2013-11-18
        • 2011-06-14
        • 1970-01-01
        相关资源
        最近更新 更多