我对 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”不同的流,但没有一个能按我的意愿工作。
知道如何解决这个问题吗?
因此,您有多种方法,以下是我的两种做法:
在此解决方案中,您使用行为主体来控制运动,并简单地过滤何时聆听。
如果我不太了解运算符,我会使用这种方法:它很容易理解并且效果很好。
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'));