RxJS 的多个关键监听器
Multiple key listeners with RxJS
我有一个物理引擎,它随每一帧更新,我有一辆带轮子的车辆,我想使用一些箭头键来控制它。
作为一种蛮力方法,我创建了 4 个 Listeners
,每个箭头键一个:
当按下一个键时...
fromEvent(document, KEY_DOWN)
.pipe(
filter(e => e.keyCode === KEYS.UP),
filter(e => !e.repeat)
)
.subscribe(() => {
...
updateSpeed(...)
});
当释放一个键时...
fromEvent(document, KEY_UP)
.pipe(filter(e => e.keyCode === KEYS.UP))
.subscribe(() => {
...
updateSpeed(...) // set speed back to 0
});
它有效,但看起来很乏味,因为我必须为每个键创建一个 keydown
监听器和一个 keyup
监听器(总共 8 个监听器)。
有没有办法合并事件,有一个更优雅的控制器?
是的,您应该查看 combineLatest
或组合运算符之一 (https://scotch.io/tutorials/rxjs-operators-for-dummies-forkjoin-zip-combinelatest-withlatestfrom)。
这不是一个完整的答案,但可以帮助您入门
import { combineLatest } from 'rxjs';
...
x = combineLatest(
fromEvent(document, KEY_DOWN).pipe(
filter(e => e.keyCode === KEYS.UP || /* Allow the other 3 keys with or statements */),
filter(e => !e.repeat),
startWith(null),
),
fromEvent(document, KEY_UP).pipe(
filter(e => e.keyCode === KEYS.UP || /* Allow the other 3 keys with or statements */)),
startWith(null),
)
).subscribe(([keyDown, keyUp]) => {
// you have access to keyDown and keyUp events here. Make sure they are truthy since we startWith null and see their values and do the appropriate actions
});
....
x.unsubscribe();
I startWith
null 因为 combineLatest
,每个 observable 必须至少发出一次流才能开始,所以这将启动流。您可能需要尝试一下才能获得想要的结果。
此外,由于您正在订阅 events
,因此从订阅中 unsubscribe
对您来说非常重要,因为事件不是有限的,这可能会导致意外行为(订阅对每个键按钮,即使此代码不是 "alive")。
============================编辑================ ======
我认为 merge
是更好的方法。
import { merge } from 'rxjs';
x = merge(
fromEvent(document, KEY_DOWN).pipe(
filter(e => e.keyCode === KEYS.UP || /* Allow the other 3 keys with or statements */),
filter(e => !e.repeat),
),
fromEvent(document, KEY_UP).pipe(
filter(e => e.keyCode === KEYS.UP || /* Allow the other 3 keys with or statements */)),
)
).subscribe(keyUpOrKeyDown => {
// now you have a handle on the event and can do the appropriate action accordingly
});
....
x.unsubscribe();
我觉得这种方式更好,因为你不需要 startWith(null)
我有一个物理引擎,它随每一帧更新,我有一辆带轮子的车辆,我想使用一些箭头键来控制它。
作为一种蛮力方法,我创建了 4 个 Listeners
,每个箭头键一个:
当按下一个键时...
fromEvent(document, KEY_DOWN)
.pipe(
filter(e => e.keyCode === KEYS.UP),
filter(e => !e.repeat)
)
.subscribe(() => {
...
updateSpeed(...)
});
当释放一个键时...
fromEvent(document, KEY_UP)
.pipe(filter(e => e.keyCode === KEYS.UP))
.subscribe(() => {
...
updateSpeed(...) // set speed back to 0
});
它有效,但看起来很乏味,因为我必须为每个键创建一个 keydown
监听器和一个 keyup
监听器(总共 8 个监听器)。
有没有办法合并事件,有一个更优雅的控制器?
是的,您应该查看 combineLatest
或组合运算符之一 (https://scotch.io/tutorials/rxjs-operators-for-dummies-forkjoin-zip-combinelatest-withlatestfrom)。
这不是一个完整的答案,但可以帮助您入门
import { combineLatest } from 'rxjs';
...
x = combineLatest(
fromEvent(document, KEY_DOWN).pipe(
filter(e => e.keyCode === KEYS.UP || /* Allow the other 3 keys with or statements */),
filter(e => !e.repeat),
startWith(null),
),
fromEvent(document, KEY_UP).pipe(
filter(e => e.keyCode === KEYS.UP || /* Allow the other 3 keys with or statements */)),
startWith(null),
)
).subscribe(([keyDown, keyUp]) => {
// you have access to keyDown and keyUp events here. Make sure they are truthy since we startWith null and see their values and do the appropriate actions
});
....
x.unsubscribe();
I startWith
null 因为 combineLatest
,每个 observable 必须至少发出一次流才能开始,所以这将启动流。您可能需要尝试一下才能获得想要的结果。
此外,由于您正在订阅 events
,因此从订阅中 unsubscribe
对您来说非常重要,因为事件不是有限的,这可能会导致意外行为(订阅对每个键按钮,即使此代码不是 "alive")。
============================编辑================ ======
我认为 merge
是更好的方法。
import { merge } from 'rxjs';
x = merge(
fromEvent(document, KEY_DOWN).pipe(
filter(e => e.keyCode === KEYS.UP || /* Allow the other 3 keys with or statements */),
filter(e => !e.repeat),
),
fromEvent(document, KEY_UP).pipe(
filter(e => e.keyCode === KEYS.UP || /* Allow the other 3 keys with or statements */)),
)
).subscribe(keyUpOrKeyDown => {
// now you have a handle on the event and can do the appropriate action accordingly
});
....
x.unsubscribe();
我觉得这种方式更好,因为你不需要 startWith(null)