我想检测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>