实现基于切换的页面旋转 rxjs Angular
Implement toggle-based page rotation rxjs Angular
对于一个项目,我有一个包含许多卡片的简单容器。由于有很多卡片,有多个页面,当屏幕显示时,它会自动旋转到下一页每 X 秒 OR 当用户点击 space 栏时.
对于一项新功能,我们添加了一个切换按钮来关闭自动旋转。我已经为 toggle-button
创建了一个 EventEmitter
,如下面的 toggleEmitter
所示,但我对 rxjs 还很陌生,不知道如何使用它来真正停止旋转。有人可以帮忙吗?
@Component({
selector: 'rotator-container',
templateUrl: './rotator-container.component.html',
})
export class RotatorContainerComponent implements AfterViewInit, OnDestroy {
@ContentChildren(RotatorItemComponent, { read: ElementRef })
rotatorItems: QueryList<ElementRef>;
@Input() rotationInterval = 30 * 1000;
@Output() toggleEmitter: EventEmitter<MatSlideToggleChange> =
new EventEmitter();
toggle(event: MatSlideToggleChange) {
this.toggleEmitter.emit(event);
}
timer$ = this.activatedRoute.queryParams.pipe(
map(params => params['rotate']),
switchMap(rotate =>
rotate === 'false' ? of(0) : timer(0, this.rotationInterval)
)
);
spaceCounter$ = fromEvent<KeyboardEvent>(document, 'keydown').pipe(
filter(({ code }) => code === 'Space'),
tap(e => e.preventDefault()),
map(() => 1),
scan((acc, curr) => acc + curr, 0),
startWith(0)
);
rotationCounter$ = combineLatest([this.timer$, this.spaceCounter$]).pipe(
map(([index, offset]) => index + offset)
);
rotatorSubscription: Subscription;
constructor(private activatedRoute: ActivatedRoute) {}
ngAfterViewInit() {
const rotatorItemsLength$ = this.rotatorItems.changes.pipe(
map(() => this.rotatorItems.length),
startWith(this.rotatorItems.length)
);
const visibleIndex$ = combineLatest([
this.rotationCounter$,
rotatorItemsLength$,
]).pipe(
map(([index, length]) => index % length),
startWith(0)
);
this.rotatorSubscription = visibleIndex$.subscribe(visibleIndex =>
this.rotatorItems.forEach((item, index) => {
(<HTMLElement>item.nativeElement).style.visibility =
visibleIndex === index ? 'visible' : 'hidden';
(<HTMLElement>item.nativeElement).style.position =
visibleIndex === index ? 'relative' : 'absolute';
})
);
}
ngOnDestroy() {
this.rotatorSubscription && this.rotatorSubscription.unsubscribe();
}
}
我认为您不需要 EventEmitter。相反,您需要更改 timer$
Observable 的工作方式。
我会尝试以下方法。
首先定义一个 Subject
,只要切换按钮状态发生变化,它就会发出 true
或 false
。特别是,如果切换按钮打开,它会发出 true
,否则会发出 false
。
这段代码看起来像这样
// use a BehaviorSubject to specify a value which gets emitted at the start, in this case false
toggleSubject = new BehaviourSubject<bool>(false)
....
toggle(event: MatSlideToggleChange) {
this.toggleSubject.next(event.checked);
}
然后更改 timer$
Observable。一种处理方式如下。
// first we just rename the current timer$ to _timer$
_timer$ = this.activatedRoute.queryParams.pipe(
map(params => params['rotate']),
switchMap(rotate =>
rotate === 'false' ? of(0) : timer(0, this.rotationInterval)
)
);
// then we redefine timer$ stream starting from toggleSubject
timer$ = toggleSubject.pipe(
// any time toggleSubject emits we switch to a new stream
switchMap(toggleVal => {
// if toggleButton is on then the automatic rotation is off otherwise is on
// this is accomplished by returning an Observable that emits 0 in the first case
// or the old definition of timer$ in the second case
return toggleVal ? of(0) : this._timer$
})
)
对于一个项目,我有一个包含许多卡片的简单容器。由于有很多卡片,有多个页面,当屏幕显示时,它会自动旋转到下一页每 X 秒 OR 当用户点击 space 栏时.
对于一项新功能,我们添加了一个切换按钮来关闭自动旋转。我已经为 toggle-button
创建了一个 EventEmitter
,如下面的 toggleEmitter
所示,但我对 rxjs 还很陌生,不知道如何使用它来真正停止旋转。有人可以帮忙吗?
@Component({
selector: 'rotator-container',
templateUrl: './rotator-container.component.html',
})
export class RotatorContainerComponent implements AfterViewInit, OnDestroy {
@ContentChildren(RotatorItemComponent, { read: ElementRef })
rotatorItems: QueryList<ElementRef>;
@Input() rotationInterval = 30 * 1000;
@Output() toggleEmitter: EventEmitter<MatSlideToggleChange> =
new EventEmitter();
toggle(event: MatSlideToggleChange) {
this.toggleEmitter.emit(event);
}
timer$ = this.activatedRoute.queryParams.pipe(
map(params => params['rotate']),
switchMap(rotate =>
rotate === 'false' ? of(0) : timer(0, this.rotationInterval)
)
);
spaceCounter$ = fromEvent<KeyboardEvent>(document, 'keydown').pipe(
filter(({ code }) => code === 'Space'),
tap(e => e.preventDefault()),
map(() => 1),
scan((acc, curr) => acc + curr, 0),
startWith(0)
);
rotationCounter$ = combineLatest([this.timer$, this.spaceCounter$]).pipe(
map(([index, offset]) => index + offset)
);
rotatorSubscription: Subscription;
constructor(private activatedRoute: ActivatedRoute) {}
ngAfterViewInit() {
const rotatorItemsLength$ = this.rotatorItems.changes.pipe(
map(() => this.rotatorItems.length),
startWith(this.rotatorItems.length)
);
const visibleIndex$ = combineLatest([
this.rotationCounter$,
rotatorItemsLength$,
]).pipe(
map(([index, length]) => index % length),
startWith(0)
);
this.rotatorSubscription = visibleIndex$.subscribe(visibleIndex =>
this.rotatorItems.forEach((item, index) => {
(<HTMLElement>item.nativeElement).style.visibility =
visibleIndex === index ? 'visible' : 'hidden';
(<HTMLElement>item.nativeElement).style.position =
visibleIndex === index ? 'relative' : 'absolute';
})
);
}
ngOnDestroy() {
this.rotatorSubscription && this.rotatorSubscription.unsubscribe();
}
}
我认为您不需要 EventEmitter。相反,您需要更改 timer$
Observable 的工作方式。
我会尝试以下方法。
首先定义一个 Subject
,只要切换按钮状态发生变化,它就会发出 true
或 false
。特别是,如果切换按钮打开,它会发出 true
,否则会发出 false
。
这段代码看起来像这样
// use a BehaviorSubject to specify a value which gets emitted at the start, in this case false
toggleSubject = new BehaviourSubject<bool>(false)
....
toggle(event: MatSlideToggleChange) {
this.toggleSubject.next(event.checked);
}
然后更改 timer$
Observable。一种处理方式如下。
// first we just rename the current timer$ to _timer$
_timer$ = this.activatedRoute.queryParams.pipe(
map(params => params['rotate']),
switchMap(rotate =>
rotate === 'false' ? of(0) : timer(0, this.rotationInterval)
)
);
// then we redefine timer$ stream starting from toggleSubject
timer$ = toggleSubject.pipe(
// any time toggleSubject emits we switch to a new stream
switchMap(toggleVal => {
// if toggleButton is on then the automatic rotation is off otherwise is on
// this is accomplished by returning an Observable that emits 0 in the first case
// or the old definition of timer$ in the second case
return toggleVal ? of(0) : this._timer$
})
)