Подтвердить что ты не робот

Можно ли переопределить задержку повторного запуска, в JavaScript?

Объект вручную устанавливает удерживаемый ключ "частота повторения".

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

Через исследования я придумал попытку на основе таймера, но в Safari он не повторяет символ. У меня есть система меню, в которой удерживается стрелка вниз по списку, но анимация перевода и частота повторения не нравятся друг другу.

var repeating = false;
var repeatRateTimer = null;

$( document ).bind( 'keyup', function( input ) {
    if( repeatRateTimer != null )
    {
        clearTimeout( repeatRateTimer );
        repeatRateTimer = null;
    }

    repeating = false;
} );

$( document ).bind( 'keydown', function( input ) {
    input.preventDefault( );

    if( repeating == true )
    {
        if( repeatRateTimer != null )
        {
            clearTimeout( repeatRateTimer );
            repeatRateTimer = null;
        }
        else
        {
            repeatRateTimer = setTimeout( function( ){ repeating = false; }, 1000 );
        }

        return;
    }
    repeating = true;

    // ...keyboard logic
} );

Возможно, я все это испортил... Я попытался воссоздать упрощенную версию этого сообщения SO. Тем не менее, я считаю, что должен быть лучший способ сделать это. Любые мысли?

Update:

Мы можем предположить, что конечный пользователь не установил скорость повторения клавиатуры ОС больше, чем скорость, которую я хочу использовать (1000 мс). Если это так, тогда он должен вернуться к своей частоте повтора, так как он не будет продолжать запускать ключевое событие пресса. Если это не так (более вероятно, так как большинство людей не модифицируют это), то мы будем переопределять это поведение, чтобы задержать наш указанный период.

4b9b3361

Ответ 1

Хорошо, я понял, почему мой пример не был циклическим. В цикле keydown он очищал таймаут до истечения срока его действия:

if( repeatRateTimer != null )
{
    clearTimeout( repeatRateTimer );
    repeatRateTimer = null;
}
else
{
    repeatRateTimer = setTimeout( function( ){ repeating = false; }, 1000 );
}

Тайм-аут следует очистить только после истечения срока действия, поэтому условие if необходимо переместить в функцию таймаута:

if( repeatRateTimer == null )
{
    repeatRateTimer = setTimeout( function( ) {
        repeating = false;
        clearTimeout( repeatRateTimer );
        repeatRateTimer = null;
    }, 1000 );
}

Я оставлю эту щедрость открытой, если кто-то может улучшить ее или предоставить лучшую альтернативу.

Ответ 2

Посмотрите на следующий файл JavaScript. Если вы прокрутите страницу до строки 530, вы найдете следующий класс:

var Keyboard = new Class(function (constructor) {
    var key = {};

    var eventListener = {
        keyup: {},
        keydown: {},
        keypress: {}
    };

    constructor.overload(["Number"], function (interval) {
        setInterval(keypress, interval);
    });

    window.addEventListener("keyup", keyup, false);
    window.addEventListener("keydown", keydown, false);

    function keyup(event) {
        var keyCode = event.keyCode;
        var listener = eventListener.keyup[keyCode];
        key[keyCode] = false;
        if (listener)
        listener();
    }

    function keydown(event) {
        var keyCode = event.keyCode;
        var listener = eventListener.keydown[keyCode];
        key[keyCode] = true;
        if (listener)
        listener();
    }

    function keypress() {
        for (var code in key) {
            var listener = eventListener.keypress[code];
            if (key[code] && listener) listener();
        }
    }

    this.addEventListener = new Dispatcher(["String", "Number", "Function"], function (type, keyCode, listener) {
        type = eventListener[type];
        if (type) type[keyCode] = listener;
        else throw new Error("Unexpected value for type.");
    });
});

Что автор сделал, так это то, что он создал специальный класс Keyboard для делегирования ключевых событий: keyup, keydown и keypress. Класс имеет только один конструктор, который принимает один аргумент - интервал события keypress (который вы хотите). Вы можете добавить прослушиватели событий, используя метод addEventListener экземпляра класса Keyboard:

var keyboard = new Keyboard(125); // fire key press 8 times a second.

keypress.addEventListener("keypress", 65, function () {
    // do something every time A is pressed
});

Обратите внимание, что указанный класс зависит от следующей структуры: Lambda JS. Вы можете увидеть рабочую демонстрацию приведенного выше script здесь. Надеюсь, это поможет.

Обновление 1:

Ваш код не работает в Opera. Кроме того, второе событие срабатывает после дополнительной задержки в 500 мс в Firefox, и последовательные события не поддерживают один и тот же интервал. Кроме того, он не может одновременно обрабатывать несколько ключевых событий. Позвольте исправить эту проблему:

Сначала нам нужно создать простой script для Delta Timing, чтобы ключевые события срабатывают после постоянного интервала. Мы используем следующий фрагмент для создания DeltaTimer:

function DeltaTimer(render, interval) {
    var timeout;
    var lastTime;

    this.start = start;
    this.stop = stop;

    function start() {
        timeout = setTimeout(loop, 0);
        lastTime = Date.now();
        return lastTime;
    }

    function stop() {
        clearTimeout(timeout);
        return lastTime;
    }

    function loop() {
        var thisTime = Date.now();
        var deltaTime = thisTime - lastTime;
        var delay = Math.max(interval - deltaTime, 0);
        timeout = setTimeout(loop, delay);
        lastTime = thisTime + delay;
        render(thisTime);
    }
}

Затем мы пишем логику для запуска пользовательских событий keypressed. Нам нужны специальные события, так как мы должны иметь возможность обрабатывать несколько ключей одновременно:

(function (interval) {
    var keyboard = {};

    window.addEventListener("keyup", keyup, false);
    window.addEventListener("keydown", keydown, false);

    function keyup(event) {
        keyboard[event.keyCode].pressed = false;
    }

    function keydown(event) {
        var keyCode = event.keyCode;
        var key = keyboard[keyCode];

        if (key) {
            if (!key.start)
                key.start = key.timer.start();
            key.pressed = true;
        } else {
            var timer = new DeltaTimer(function (time) {
                if (key.pressed) {
                    var event = document.createEvent("Event");
                    event.initEvent("keypressed", true, true);
                    event.time = time - key.start;
                    event.keyCode = keyCode;
                    window.dispatchEvent(event);
                } else {
                    key.start = 0;
                    timer.stop();
                }
            }, interval);

            key = keyboard[keyCode] = {
                pressed: true,
                timer: timer
            };

            key.start = timer.start();
        }
    }
})(1000);

interval устанавливается в 1000 ms, но вы можете это изменить. Наконец, чтобы зарегистрировать событие, мы делаем:

window.addEventListener("keypressed", function (event) {
    document.body.innerHTML += event.keyCode + " (" + event.time + " ms)<br/>";
}, false);

Это простой и эффективный JavaScript. Не требуется jQuery. Здесь вы можете увидеть живую демонстрацию и посмотреть разницу между вашим script и моим. Приветствия.

Обновление 2:

Глядя на другой вопрос в StackOverflow, так вы можете реализовать его, используя выше шаблон:

window.addEventListener("keypressed", function (event) {
    switch (event.keyCode) {
    case 37:
        Move(-1, 0);
        break;
    case 38:
        Move(0, -1);
        break;
    case 39:
        Move(1, 0);
        break;
    case 40:
        Move(0, 1);
        break;
    }
}, false);

Использование вышеуказанного кода приведет к удалению короткой задержки, которую вы испытываете, а также позволяет одновременно запускать несколько событий для разных ключей.

Ответ 3

Как насчет создания пользовательских ключевых событий. вы можете прослушивать оригинальные (keyup/keydown), и если они передают условие времени, вы запускаете свое пользовательское событие.
Этот способ имеет то преимущество, что вы не полагаетесь на таймеры, и это дает вам больше силы, потому что вы используете пользовательские события (кстати, вы можете пропустить часть события отмены, если хотите).
Вот демо, чтобы посмотреть, о чем я говорю: http://jsfiddle.net/gion_13/gxEMz/
И базовый код выглядит примерно так:

$(document).ready(function(){
    var dispatcher = $(window),
        keyRate = 1000, //ms
        lastKeyEvent = 0,
        cancelEvent = function(e){
            var evt = e ? e:window.event;
            if(evt.stopPropagation)    
                evt.stopPropagation();
            if(evt.cancelBubble!=null) 
                evt.cancelBubble = true;
            return false;
        };

    dispatcher
        .bind('keydown',function(e){
            var now = new Date().getTime();
            if(now - lastKeyEvent <= keyRate)
                // cancel the event
                return cancelEvent(e);
            var keyEventsTimeDiff = now - lastKeyEvent;
            lastKeyEvent = now;
            dispatcher.trigger('special-keydown',[e,keyEventsTimeDiff ]);
        })
        .bind('keyup',function(e){
            cancelEvent(e);
            dispatcher.trigger('special-keyup',[e]);
        })
        // binding the custom events
        .bind('special-keydown',function(e,keyEventsTimeDiff){
            console.log(e,'special keydown triggered again after ' + keyEventsTimeDiff +'ms');
        })
       .bind('special-keyup',function(e,keyEventsTimeDiff){
            console.log(e,'special keyup');
        });
});