Использование противодавления для нажатия клавиш в RxJs
Я пытаюсь захватить нажатие клавиши пользователя с помощью RxJS, и для каждого штриха, сгенерировать объект результата, который содержит ключ, продолжительность штриха (время между keyup
а также keydown
события) и интервал между предыдущими штрихами (используя timeInterval
). Смотрите изображение ниже для обзора.
Пока мой код работает: Hello
выходы ShiftLeftHELLO
,
Но когда я пишу быстрее (как обычно, я имею в виду), все разбивается и World
выходы ShiftLeftShiftLeftOLD
,
Есть ли у вас какие-либо предложения по реализации обратного давления, буферизации или что-то еще в моем коде, чтобы предотвратить это поведение?
(function (document) {
var textarea = document.querySelector("#input");
var keyUpStream = Rx.DOM.keyup(textarea);
var keyDownStream = Rx.DOM.keydown(textarea);
var keyStrokeStream = Rx.Observable.merge(keyDownStream, keyUpStream);
var keystroke = keyStrokeStream.filter((function() {
var keysPressed = {};
return function(e) {
var k = e.which;
if (e.type == 'keyup') {
delete keysPressed[k];
return true;
} else if (e.type == 'keydown') {
if (keysPressed[k]) {
return false;
} else {
keysPressed[k] = true;
return true;
}
}
};
})())
.distinctUntilChanged(function (e){
return e.type + e.which;
})
.timeInterval()
.bufferWithCount(2)
.zip(function (evts){
return {
"ts" : Date.now(),
"key": evts[0].value.code,
"evts" : evts,
"duration" : evts.reduce(function(a, b){
return b.value.timeStamp - a.value.timeStamp;
})
};
}).subscribe(function (e){
console.log(e);
document.querySelector("#output").textContent += e.key.replace("Key", '');
document.querySelector("#console").textContent += JSON.stringify(e);
});
})(document);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs-dom/7.0.3/rx.dom.js"></script>
<h1>KeyStroke</h1>
<textarea id="input" rows="5" cols="50"></textarea>
<div id="output"></div>
<div id="console"></div>
1 ответ
Отказ от ответственности: Rxjs нуб здесь.
Проблема в том, что ваш код ожидает ключ от ключа после ключа от того же ключа. Когда вы печатаете быстро, у вас возникает состояние гонки при нажатии клавиш, в результате чего создается буфер с несколькими нажатиями клавиш или сочетаниями клавиш. Не то, что вы ожидаете в своем буфере.
Я изменил код, чтобы воспользоваться вашей функцией фильтра. Эта функция возвращает только события нажатия клавиш и сохраняет соответствующий ход нажатия клавиш. Таким образом, вы вычисляете интервал в функции фильтра. Я уверен, что есть более элегантное решение, использующее только функции RxJS, но так как вы уже использовали состояние в функции фильтра, я немного его изменил.
Фрагмент теперь показывает правильный вывод. Я думаю, что это решает вашу проблему, но это не очень хороший способ использования RxJS (как я уже говорил, мы сохраняем состояние в функции фильтра).
(function (document) {
var textarea = document.querySelector("#input");
var keyUpStream = Rx.DOM.keyup(textarea);
var keyDownStream = Rx.DOM.keydown(textarea);
var keyStrokeStream = Rx.Observable.merge(keyDownStream, keyUpStream);
var keystroke = keyStrokeStream.filter((function() {
var keysPressed = {};
return function(e) {
var key = e.which;
var result;
if (e.type == 'keyup' && keysPressed.hasOwnProperty(key)) {
e.strokeInterval = Date.now() - keysPressed[key];
delete keysPressed[key];
return true;
} else if (e.type == 'keydown') {
if (!keysPressed.hasOwnProperty(key)) {
keysPressed[key] = Date.now();
}
return false;
}
return false;
};
})())
.map(function (evt){
return {
"ts" : Date.now(),
"key": evt.code,
"duration" : evt.strokeInterval
};
})
.subscribe(function (e){
console.log(e);
document.querySelector("#output").textContent += e.key.replace("Key", '');
document.querySelector("#console").textContent += JSON.stringify(e);
});
})(document);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs-dom/7.0.3/rx.dom.js"></script>
<h1>KeyStroke</h1>
<textarea id="input" rows="5" cols="50"></textarea>
<div id="output"></div>
<div id="console"></div>