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)