这是我想出的:
它在 Angular Zone 之外运行。当鼠标移动时,不会给您的应用程序带来变化检测的负担。它仅在视频停止或继续时触发变化检测。
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable, NgZone } from '@angular/core';
import {
BehaviorSubject,
fromEvent,
merge,
Observable,
of,
Subject,
} from 'rxjs';
import {
distinctUntilChanged,
mapTo,
startWith,
switchMapTo,
debounceTime,
takeUntil,
} from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class VideoService {
private readonly onDestroy = new Subject<void>();
readonly isPlaying$: Observable<boolean> = new BehaviorSubject(true);
constructor(
@Inject(DOCUMENT) private readonly document: Document,
private readonly ngZone: NgZone
) {
// Run the whole logic outside of angular, so it won't burdent the app
this.ngZone.runOutsideAngular(() => {
fromEvent(this.document, 'click', { passive: true })
.pipe(
// On every click we create a merge observable, and unsubscribe from the previous one if exists
switchMapTo(
// We merge two observables
merge(
// one that sets the playing to false instantly
of(false),
// Subscribe to the documents mousemove
fromEvent(this.document, 'mousemove', { passive: true }).pipe(
// we start the stream with a value, so after the clicking it will go back to playing without moving the cursor.
startWith(null),
// With debounceTime we wait for no mousemove
debounceTime(1000),
// All values are mapped to true
mapTo(true)
)
)
),
distinctUntilChanged(),
takeUntil(this.onDestroy)
)
.subscribe((value) =>
// when we get a different value run it in the zone, so Angular will know to run change detection.
this.ngZone.run(() => {
(this.isPlaying$ as BehaviorSubject<boolean>).next(value);
})
);
});
}
ngOnDestroy() {
this.onDestroy.next();
this.onDestroy.complete();
}
}
Running example
我对其他解决方案感兴趣,因为我的解决方案似乎并不简单。我觉得它也可以做得更简单。