【问题标题】:How do I conditionally buffer key input based on event in RxJs如何根据 RxJs 中的事件有条件地缓冲键输入
【发布时间】:2017-04-05 17:11:15
【问题描述】:

我对 RxJs 很陌生,还没有阅读过解决方案。更详细的解释在 cmets 中,但基本上我想在按下特定键时处理一个组合键(我认为缓冲区会这样做)(比如按下“o”会等待一小段时间以按下其他键) , 但否则立即处理键输入(如果“o”没有被按下,或者“o”的“超时”已过,则“o”以外的任何内容)。

Observable.fromEvent(document, 'keydown')
  // Now I want to only continue processing the event if the user pressed "o99" in series,
  // as in pressed "o", followed by "9" and then another "9"
  // I think I can do it with buffer
  .buffer(() => Observable.timer(1000))
  .map((e) => 'option-99')
  // However I need to pass the keys through unbuffered if it's anything but an "o" (unless it is following an "o")
  // In other words, "o99" is buffered or something, but "9" is not, and processed immediately
  .map((e) => e.keyCode)

谢谢

【问题讨论】:

  • 花了我一段时间,但参见。我的答案是用小型状态机实现逻辑

标签: javascript events rxjs observable reactivex


【解决方案1】:

您有一个非常常见的情况需要处理,即相关操作取决于某些控制状态的事件(即您有一个基本状态机)。基本上,如果您考虑这两个控制状态:SIMPLE_KEYCOMBO,以及这两个事件:keyupkeyupTimer,那么:

  • 处于SIMPLE_KEY 状态
    • 如果按下的键是o,则切换到COMBO状态
    • 如果不是o,那么我们保持相同的状态,并将密钥传递给下游
    • 如果是定时器事件,那么我们传递一个空值被下游过滤掉
  • 处于COMBO 状态
    • 我们累积按下的键
    • 如果定时器事件,那么我们恢复到SIMPLE_KEY状态

因此,要将您的事件映射到一个动作,您需要知道您所处的控制状态。运算符scan 允许您根据累积的状态决定如何处理事件。

可能是这样的 (https://jsfiddle.net/cbhmxaas/):

function getKeyFromEvent(ev) {return ev.key}
function isKeyPressedIsO(ev) {return getKeyFromEvent(ev) === 'o'}

const KEY = 'key';
const TIMER = 'timer';
const COMBO = 'combo';
const SIMPLE_KEY = 'whatever';
const timeSpanInMs = 1000;
const keyup$ = Rx.Observable.fromEvent(document, 'keyup')
  .map(ev => ({event: KEY, data: getKeyFromEvent(ev)}));
const keyupTimer$ = keyup$.flatMapLatest(eventStruct => 
  Rx.Observable.of(eventStruct)
//    .tap(console.warn.bind(console, 'timer event'))
    .delay(timeSpanInMs)
    .map(() => ({event : TIMER, data: null}))
    );

Rx.Observable.merge(keyup$, keyupTimer$)
//  .tap(console.warn.bind(console))
  .scan((acc, eventStruct) => {
    if (acc.control === SIMPLE_KEY) {
      if (eventStruct.event === KEY) {
        if (eventStruct.data === `o`) {
          return {control: COMBO, keyOrKeys : []}
        }
        else {
          return {control: SIMPLE_KEY, keyOrKeys : eventStruct.data}
        }
      }
      else {
      // TIMER event
        return {control: SIMPLE_KEY, keyOrKeys : null}
      }
    }
    else {
      // we only have two states, so it is COMBO state here
      if (eventStruct.event === KEY) {
        return {control: COMBO, keyOrKeys : acc.keyOrKeys.concat([eventStruct.data])}
      }
      else {
        // this is a TIMER event, we only have two events
        return {control: SIMPLE_KEY, keyOrKeys : acc.keyOrKeys}
      }
    }
  }, {control: SIMPLE_KEY, keyOrKeys : null})
//  .tap(console.warn.bind(console))
  .filter(state => state.keyOrKeys) // TIMER event in SIMPLE_KEY state
  .map (({control, keyOrKeys}) => {
  // Here you associate your action
  // if keyOrKeys is an array, then you have a combo
  // else you have a single key
  console.log('key(s) pressed', keyOrKeys)
  return keyOrKeys
})
  .subscribe (console.log.bind(console))

【讨论】:

    猜你喜欢
    • 2017-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-04
    • 1970-01-01
    • 1970-01-01
    • 2014-04-16
    相关资源
    最近更新 更多