Использование противодавления для нажатия клавиш в 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>

Streams

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>

Другие вопросы по тегам