Слушайте несколько нажатий клавиш - PullRequest
8 голосов
/ 30 сентября 2011

Я пытаюсь позволить пользователю перемещать элемент на странице с помощью клавиш со стрелками.Пока что у меня есть движение, работающее вверх / вниз / влево / вправо, но не по диагонали (две клавиши со стрелками нажаты одновременно).

Мой слушатель выглядит так:

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

Идея заключалась в том, что если пользователь нажимает клавишу со стрелкой, он устанавливает x и y на negative или positive и запускает функцию move (), которая перемещает элемент на заданное количество пикселей вжелаемое направление, и что, если две клавиши были нажаты, второе событие сработало бы ... Я также надеюсь, что пользователь сможет, по-видимому, безошибочно изменить направление, быстро отпуская и нажимая клавиши. Однако, если пользователь не выполнит ни одного из этих действий,нажимая другую клавишу со стрелкой, они, похоже, должны ждать, пока произойдет движение, если только они полностью не отпустят клавишу, а затем нажмут другую, и она не будет реагировать на вторую клавишу вообще, пока первая не будет отпущена.

Ответы [ 3 ]

9 голосов
/ 30 сентября 2011

Скрипка: http://jsfiddle.net/ATUEx/

Создайте временный кеш для запоминания ваших нажатий клавиш.

Реализация обработки двух ключей будет выглядеть следующим образом:

  1. <keydown>
    • Очистить предыдущий тайм-аут.
    • Проверьте, был ли кеширован код клавиши или нет.
      Если да, и допустимая комбинация:
      - Удалить все кэшированные коды клавиш
      - Выполнить функцию для этой комбинации
      еще
      - Удалить все кэшированные коды клавиш
      - Сохранить новый код ключа
      - Установите время для сброса кодов клавиш (см. Ниже) с разумной задержкой
  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) области. Если вам не нужна область видимости, не стесняйтесь удалить первую и последнюю строку.

2 голосов
/ 30 сентября 2011

Вот вам ваш код, слегка пересмотренный ... http://jsfiddle.net/w8uNz/ Это просто РАБОТАЕТ.Перемещает элемент по родительскому элементу.

Роб W прав, у вас должен быть динамический кеш нажатий клавиш в определенный период времени.Но для чего ?если вы делаете динамическую игру - для этого должна быть гораздо более высокая абстракция.И в целом, вам нужно улучшить свой уровень кодирования, если вы хотите делать такие вещи.(Или это просто вызовет у вас головную боль.)

1 голос
/ 30 сентября 2011

Логически это звучит довольно просто:

  1. первый ключ запускает событие нажатия клавиши, сохраните это событие в стеке
  2. затем второй ключ вызывает событие нажатия клавиши вниз, сохраняя это событие в стеке
  3. теперь у вас есть два нажатия клавиш и ни одного нажатия вверх
  4. если ключ сработал, у вас есть три возможности
    1. у вас есть только одно событие => идти в этом направлении
    2. у вас есть два события => идти по диагонали
    3. у вас более двух событий или неверный параметр (например, вправо и влево) => ничего не делать, или как вам нравится
  5. очистить стек
...