RxJS-检测长鼠标按下



我想检测mousedown何时被发射超过500ms,如果是,请采取措施。我的尝试:

const button = document.querySelector('button')
const stream = Rx.Observable.fromEvent(button, 'mousedown')
const mouseUp$ = Rx.Observable.fromEvent(button, 'mouseup')
stream.delay(500).takeUntil(mouseUp$).subscribe(() => console.log(1))

它工作,但只是第一次运行。然后,由于takeUntil运算符,流被取消。如何让它每次都起作用?

演示

在每个mouseDown$事件上启动一个TimerObservable,持续500ms。如果mouseUp$在500ms内从TimerObservable发射unsubscribe

const button = document.querySelector('button')
const mouseDown$ = Rx.Observable.fromEvent(button, 'mousedown')
const mouseUp$ = Rx.Observable.fromEvent(button, 'mouseup')
const stream$ = mouseDown$.switchMap(() => Rx.Observable.TimerObservable(500).takeUntil(mouseUp$));
stream$.subscribe(() => console.log('Only Fired after 500ms'))

RxJS>=6.0

import { switchMap, takeUntil } from 'rxjs/operators';
import { timer, fromEvent } from 'rxjs';
const button = document.querySelector('button')
const mouseDown$ = fromEvent(button, 'mousedown')
const mouseUp$ = fromEvent(button, 'mouseup')
const stream$ = mouseDown$.pipe(
switchMap(() => timer(500).pipe(takeUntil(mouseUp$)))
);
stream$.subscribe(() => console.log('Only Fired after 500ms'))

鼠标保持指令示例:

@Directive({ selector: "[appMouseHold]" })
export class MouseHoldDirective implements OnInit, OnDestroy {
@Input() set appMouseHold(tick: string | number) {
if (typeof tick === 'string') {
tick = parseInt(tick, 10);
}

this.tick = tick || 500;
}
private tick: number;
private readonly _stop = new Subject<void>();
private readonly _start = new Subject<void>();
private subscription: Subscription;
@Output() mousehold = new EventEmitter<number>();
@Output() mouseholdstart = new EventEmitter<void>();
@Output() mouseholdend = new EventEmitter<void>();
ngOnInit() {
this.subscription = this._start
.pipe(
tap(() => this.mouseholdstart.emit()),
switchMap(() =>
timer(500, this.tick).pipe(
takeUntil(this._stop.pipe(tap(() => this.mouseholdend.emit())))
)
)
)
.subscribe((tick) => {
this.mousehold.emit(tick);
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
@HostListener("mousedown", ["$event"])
onMouseDown($event) {
if ($event.button === 0) {
this._start.next();
}
}
@HostListener("mouseup")
onMouseUp() {
this._stop.next();
}
}

参见Stacklitz

对于非角度使用,您可以简单地将@HostListener处理程序替换为fromEvent()可观察

SplitterAlex的答案很好,但takeUntil()完成了可观察,您无法再处理事件,所以我的解决方法是(它不完成可观察(

public touchStartSubject: Subject<any> = new Subject<any>();
public touchStartObservable: Observable<any> = this.touchStartSubject.asObservable();
public touchEndSubject: Subject<any> = new Subject<any>();
public touchEndObservable: Observable<any> = this.touchEndSubject.asObservable();
@HostListener('touchstart', ['$event'])
public touchStart($event: TouchEvent): void {
this.touchStartSubject.next($event);
}
@HostListener('touchend', ['$event'])
public touchEnd(): void {
this.touchEndSubject.next(null);
}
this.touchStartObservable
.pipe(
mergeMap((res) => race(
timer(1500).pipe(map(() => res)),
this.touchEndObservable,
)),
)
.subscribe((res: TouchEvent) => {
if (!res) return;
// do stuff
})

如果有人能在没有if (!res) return;条件的情况下改进我的答案,那就太好了,例如touchEndSubject使用.error而不是.next

Angular指令的一个例子,该指令还触发鼠标事件

import {Directive, ElementRef, Output} from "@angular/core";
import {fromEvent, merge, timer} from "rxjs";
import {filter, skip, switchMap} from "rxjs/operators";
@Directive({
selector: '[appLongClick]'
})
export class LongClickDirective {
/**
* Minimum time between mouse button down to mouse button up
*/
private readonly DUE_TIME = 500;
/**
* Mouse down event (only left button)
*/
private mousedown = fromEvent(this.el.nativeElement, 'mousedown').pipe(
filter((ev: MouseEvent) => ev.button === 0)
);
/**
* Click event (mouse left button up)
*/
private click = fromEvent(this.el.nativeElement, 'click');
/**
* After a mouse button down, take the click only if it comes after the due time
*/
@Output('appLongClick') longClick = this.mousedown.pipe(
switchMap(() =>
merge(this.click, timer(this.DUE_TIME)).pipe(
skip(1),
filter(this.isPointerEvent),
),
),
);
constructor(private el: ElementRef) {}
private isPointerEvent(v: unknown): v is PointerEvent {
return v instanceof PointerEvent;
}
}

使用示例:

<div (appLongClick)="doSomething($event)"></div>

最新更新