如何用rxjs过滤keydowns?

Fak*_*ish 7 javascript keydown rxjs

我需要得到干净的keydown/keyup事件而不重复.当你按下一个键keydown事件发生时,当你释放 - keyup.没有杂乱的重复keydowns.

这是代码:

var keyDowns = rx.Observable.fromEvent(document, 'keydown');
var keyUps = rx.Observable.fromEvent(document, 'keyup'); 
var keyActions = rx.Observable.merge(keyDowns, keyUps);

keyActions.subscribe(function(e) {
    console.log e
});
Run Code Online (Sandbox Code Playgroud)

如何适应它来完成这项工作?

Fed*_*ssi 17

虽然它很好用但是icio与状态跟踪的解决方案非常不合时宜.适当的RxJs 5解决方案是:

var keyDowns = Rx.Observable.fromEvent(document, "keydown")
var keyUps = Rx.Observable.fromEvent(document, "keyup")

var keyPresses = keyDowns
  .merge(keyUps)
  .groupBy(e => e.keyCode)
  .map(group => group.distinctUntilChanged(null, e => e.type))
  .mergeAll()
Run Code Online (Sandbox Code Playgroud)

说明:

  • 合并所有键的所有keydown和keyup事件.
  • 按键(keyCode)对它们进行分组.它创建了一个可观察的可观察对象,其中每个子可观察对象将包含一个键的所有事件.
  • 将每个sub-observable映射到其自身的distinctUntilChanged版本.按事件类型执行,以便跳过额外的keydowns.
  • 将所有键合并回一个observable

  • rxjs 6 版本会很棒 (3认同)
  • 您能否提供一个工作完全相同的可管道操作符示例?我对联合类型有问题。 (2认同)

ici*_*cio 8

查看Federico的答案,获得更优雅和惯用的解决方案.


您可以避免使用observable中的重复项(如保持键所示)distinctUntilChanged,过滤掉与最后一个发射值匹配的值(其中匹配由发射值的相等性或由keySelector回调参数转换的值确定).

在您的情况下,这可能看起来像下面这样简单:

var keyDowns = Rx.Observable.fromEvent(document, 'keydown');
var keyUps = Rx.Observable.fromEvent(document, 'keyup');
var keyActions = Rx.Observable
    .merge(keyDowns, keyUps)
    .distinctUntilChanged(function(e) { return e.type + (e.key || e.which); });

keyActions.subscribe(function(e) {
    console.log(e.type, e.key || e.which, e.keyIdentifier);
});
Run Code Online (Sandbox Code Playgroud)

但是当拿着多个钥匙时,这个不足.在Chrome OS X中,如果我按住A,S,D,然后释放A,S,DI,请在控制台中获取以下内容:

keydown 65 U+0041
keydown 83 U+0053
keydown 68 U+0044
keyup 65 U+0041
keydown 68 U+0044
keyup 83 U+0053
keydown 68 U+0044
keyup 68 U+0044
Run Code Online (Sandbox Code Playgroud)

注意释放A和S时重复"keydown 68"(D).这是因为keydown事件仅对最近按下的键重复.使用与评论中的想法类似的想法,我们可以尝试使用自定义过滤器来维护已按下哪些键的列表:

var keyDowns = Rx.Observable.fromEvent(document, 'keydown');
var keyUps = Rx.Observable.fromEvent(document, 'keyup');
var keyActions = Rx.Observable
    .merge(keyDowns, keyUps)
    .filter((function() {
        var keysPressed = {};
        return function(e) {
            var k = e.key || 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;
                }
            }
        };
    })());

keyActions.subscribe(function(e) {
    console.log(e.type, e.key || e.which, e.keyIdentifier);
});
Run Code Online (Sandbox Code Playgroud)

按住A,S,D,然后释放A,S,D现在给出:

keydown 65 U+0041
keydown 83 U+0053
keydown 68 U+0044
keyup 65 U+0041
keyup 83 U+0053
keyup 68 U+0044
Run Code Online (Sandbox Code Playgroud)

这与您在评论中提到的方法类似,不同之处在于我们正在过滤组合流而不是预过滤,以使组合流看起来像我们喜欢的那样.

我对rx.js的经验不足以断言这是最好的方法,但它确实感觉更符合Rx风格.(任何评论都赞赏.)