在 RxJS 中重新创建上下文菜单事件

Recreate contextmenu event in RxJS

我想在触摸时重新创建 contextmenu event from touch events in RxJS because iOS does not support 上下文菜单事件。

用简单的语言来说,这个 observable 应该在以下时间发出:

在以下情况下不应发出:

如果 touchend 发生得更快或者 touchstart 之后是 touchmove,我不知道如何跳过。这是我到目前为止的内容:

const touchStart$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchstart");
const touchEnd$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchend");
const touchMove$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchmove");
const contextmenu$ = touchStart$.pipe(
      switchMap(event => touchEnd$.pipe(mapTo(event))),
      switchMap(event => timer(2000).pipe(mapTo(event), takeUntil(merge(touchEnd$, touchMove$))))
);

contextmenu$.subscribe($event => {
      console.log("CONTEXTMENU EVENT HAPPENED");
});

我想出了一个可能的解决方案:

const touchStart$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchstart");
const touchEnd$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchend");
const touchMove$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchmove");
const contextmenu$ = touchStart$.pipe(
    switchMap(event =>
        timer(2000).pipe(
           mapTo(event),
           takeUntil(merge(touchMove$, touchEnd$)),
           switchMap(event => touchEnd$.pipe(mapTo(event), takeUntil(touchMove$)))
        )
    )
);

contextmenu$.subscribe($event => {
    console.log("CONTEXT MENU EVENT HAPPENED");
});

您的解决方案可以稍微简化一下。内部 mapTo 不是必需的,如果您不重复使用名称 event,您可以在管道的末尾使用一个 mapTo。此外,您可能希望在内部 switchMap 上使用 take(1) 而不是 takeUntil(touchMove$),因为您希望在第一次发射后结束流:

  const longPress$ = this.touchStart$.pipe(
    switchMap(event => timer(2000).pipe(
      takeUntil(merge(touchMove$, touchEnd$)),
      switchMap(() => touchEnd$.pipe(take(1))),
      mapTo(event)
    ))
  );

我假设你得到了你想要的行为,但我认为通常长按会在持续时间过去后触发;即我们不需要等待 touchend 事件。如果是这样,那就更简单了:

  const longPress$ = this.touchStart$.pipe(
    switchMap(event => timer(2000).pipe(
      takeUntil(merge(touchMove$, touchEnd$)),
      mapTo(event)
    ))
  );

这里有几个 StackBlitzes 和丰富多彩的日志记录:Original | Simplified