RXJS 对键盘/鼠标点击组合做出反应

问题描述 投票:0回答:1

我对 RXJS 还很陌生。我想要实现的是当用户按下空格(或任何其他)键并用鼠标拖动元素时拖动 HtmlElement。

因此拖动的开始是由 SPACE-down_LeftClick 或 LeftClick_SPACE-down 触发的。两者都被接受以方便使用。 拖动结束是通过释放 SPACE 键(keyup 事件)或单击鼠标(mouseup 事件)触发的,以先发生者为准。

实现的想法是拥有一个“dragstart”可观察对象和一个“dragend”可观察对象,我可以订阅它们。 我有 4 个与事件相对应的可观察量,根据特定键(空格)和鼠标按钮(左键)进行过滤:

spaceDown$ = fromEvent<KeyboardEvent>(document, "keydown").pipe( filter(key => key.code === 'Space'))

spaceUp$ = fromEvent<KeyboardEvent>(document, "keyup").pipe( filter( key => key.code === 'Space'))

mouseDown$ = fromEvent<MouseEvent>(document, "mousedown").pipe( filter( mouse => mouse.button == 0))

mouseUp$ = fromEvent<MouseEvent>(document, "mouseup").pipe( filter( mouse => mouse.button == 0))

我正在努力解决的是如何在 RXJS 中实现所需的行为。我尝试过“combineLatest”、“withLatestFrom”、“merge”不同的流,但没有一个能按我的意愿工作。

知道如何解决这个问题吗?

angular typescript rxjs mouseevent keyboard-events
1个回答
0
投票

因此,您有多种方法,以下是我的两种做法:

第一个:“虚拟”一个

在此解决方案中,您使用行为主体来控制运动,并简单地过滤何时聆听。

如果我不太了解运算符,我会使用这种方法:它很容易理解并且效果很好。

import {
  BehaviorSubject,
  combineLatest,
  filter,
  fromEvent,
  map,
  Observable,
  switchMap,
  throttleTime,
} from 'rxjs';

const filterSpace = (source: Observable<KeyboardEvent>) =>
  source.pipe(filter((v) => v.code === 'Space'));

const filterLeft = (source: Observable<MouseEvent>) =>
  source.pipe(filter((v) => v.button === 0));

const mousedown$ = fromEvent(document, 'mousedown').pipe(filterLeft);
const mouseup$ = fromEvent(document, 'mouseup').pipe(filterLeft);

const spacedown$ = fromEvent(document, 'keydown').pipe(filterSpace);
const spaceup$ = fromEvent(document, 'keyup').pipe(filterSpace);

const movement$ = fromEvent(document, 'mousemove');

const clicking$ = new BehaviorSubject(false);
const spacing$ = new BehaviorSubject(false);

mousedown$.subscribe(() => clicking$.next(true));
mouseup$.subscribe(() => clicking$.next(false));

spacedown$.subscribe(() => spacing$.next(true));
spaceup$.subscribe(() => spacing$.next(false));

const moving$ = movement$.pipe(
  switchMap((event) =>
    combineLatest([clicking$, spacing$]).pipe(
      filter((conds) => conds.every((v) => !!v)),
      map(() => event)
    )
  )
);

moving$.pipe(throttleTime(100)).subscribe(() => console.log('dragging'));

第二个:“高级”一个

这个有点难掌握,但我相信这就是你所要求的。

我们首先创建“启动”侦听器,它是两个条件的

combineLatest
(单击 + 空格)。我们在第一次发生时立即关闭它,然后重复它,以便我们可以......好吧,重复该行为。

注意:您也可以使用过滤器运算符,就像之前使用

.every
的解决方案一样,但是嘿,这是一个很好的灵活性,不是吗?

接下来,我们制定“停止”条件:如果满足其中一个条件,则此可观察量会发出。

与combineLatest不同,我们不需要第一个和重复,因为race不像combineLatest那样保持状态:它只是在其中一个成员发出时发出。

最后,我们将行为切换到我们想要监听的实际可观察对象(鼠标移动),我们监听直到满足停止条件,完成后我们重复,以便再次重复该行为。

import {
  combineLatest,
  filter,
  first,
  fromEvent,
  Observable,
  race,
  repeat,
  switchMap,
  takeUntil,
  throttleTime,
} from 'rxjs';

const filterSpace = (source: Observable<KeyboardEvent>) =>
  source.pipe(filter((v) => v.code === 'Space'));

const filterLeft = (source: Observable<MouseEvent>) =>
  source.pipe(filter((v) => v.button === 0));

const mousedown$ = fromEvent(document, 'mousedown').pipe(filterLeft);
const mouseup$ = fromEvent(document, 'mouseup').pipe(filterLeft);

const spacedown$ = fromEvent(document, 'keydown').pipe(filterSpace);
const spaceup$ = fromEvent(document, 'keyup').pipe(filterSpace);

const movement$ = fromEvent(document, 'mousemove');

const starting$ = combineLatest([mousedown$, spacedown$]).pipe(
  first(),
  repeat()
);
const stopping$ = race(mouseup$, spaceup$);

const moving$ = starting$.pipe(
  switchMap(() => movement$),
  takeUntil(stopping$),
  repeat()
);

moving$.pipe(throttleTime(100)).subscribe(() => console.log('dragging'));
© www.soinside.com 2019 - 2024. All rights reserved.