javascript 如何使用 rxjs 过滤按键?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31206734/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to filter keydowns with rxjs?
提问by Fake Fish
I need to get clean keydown/keyup events without repetitions. When you press a key keydown event happens, when you release - keyup. Without messy repeated keydowns.
我需要在没有重复的情况下获得干净的 keydown/keyup 事件。当您按下某个键时会发生 keydown 事件,当您松开时 - keyup。没有凌乱的重复按键。
Here's code:
这是代码:
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
});
How to adapt it to do the job?
如何适应它来完成工作?
回答by Federico Galassi
While it's nice it works, the solution by icio with state tracking is ** very ** unidiomatic. The proper RxJs 5solution would be:
虽然它很好用,但 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()
Explanation:
解释:
- Merge all keydown and keyup events for all the keys.
- Group them by key (keyCode). It creates an observable of observables where each sub-observable will contain all events for one key.
- Map each sub-observable to the distinctUntilChanged version of itself. Do it by event type, so that extra keydowns are skipped.
- Merge all the keys back to one observable
- 合并所有键的所有 keydown 和 keyup 事件。
- 按键 (keyCode) 将它们分组。它创建了一个 observables 的 observables,其中每个子 observable 将包含一个键的所有事件。
- 将每个 sub-observable 映射到它自己的 distinctUntilChanged 版本。按事件类型执行,以便跳过额外的按键操作。
- 将所有键合并回一个可观察对象
回答by icio
Check out Federico's answer below for a much more elegant and idiomatic solution.
查看下面 Federico 的答案,以获得更优雅和惯用的解决方案。
You can avoid repeated items from an observable (as you see when a key is held) with distinctUntilChanged
which filters out values matching the last emitted value (where matching is determined by equality of emitted values or their values transformed by the keySelector
callback argument).
您可以避免 observable 中的重复项(正如您在持有键时看到的那样),distinctUntilChanged
它会过滤掉与最后发出的值匹配的值(其中匹配由发出的值的相等性或其由keySelector
回调参数转换的值确定)。
In your case, this could look as simple as something like:
在您的情况下,这可能看起来很简单:
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);
});
but this falls short when holding multiple keys. In Chrome OS X, if I press and hold A, S, D, and then release A, S, D I get the following in the console:
但是当持有多个键时,这是不够的。在 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
Note the repetition of "keydown 68" (D) when releasing A and S. This is because keydown
events only repeat for the most-recently pressed key. Using a similar idea to the one in your comment, we can instead try a custom filter which maintains a list of which keys have been pressed:
请注意在释放 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);
});
Pressing and holding A, S, D, and then releasing A, S, D now gives:
按住 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
which is similar to the approach you mentioned in the comment except that we're filtering the combined stream rather than pre-filtering such that the combined stream looks as we like it.
这类似于您在评论中提到的方法,不同之处在于我们正在过滤组合流而不是预过滤,以便组合流看起来像我们喜欢的那样。
I'm not experienced enough with rx.js to assert that this is the best way to do this, but it certainly feels more in line with the Rx style. (Any comments appreciated.)
我对 rx.js 的经验不足,无法断言这是做到这一点的最佳方式,但它确实感觉更符合 Rx 风格。(任何评论表示赞赏。)
回答by Sumama Waheed
In RxJs 5, if you just want to filter a single key, it would be:
在 RxJs 5 中,如果你只想过滤一个键,它会是:
import { fromEvent } from 'rxjs/observable/fromEvent';
import { merge } from 'rxjs/operators/merge';
import { distinctUntilChanged, filter } from 'rxjs/operators';
const keyDowns = fromEvent(document, 'keydown');
const keyUps = fromEvent(document, 'keyup');
const keyPresses = keyDowns.pipe(
merge(keyUps),
filter((e: KeyboardEvent) => e.keyCode === 17),
distinctUntilChanged((x, y) => x.type == y.type)
);
keyPresses.subscribe((ctrlpress) => {
this.holdingCtrl = ctrlpress.type === 'keydown';
});