实现基于切换的页面旋转 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 ,只要切换按钮状态发生变化,它就会发出 truefalse 。特别是,如果切换按钮打开,它会发出 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$
    
  })
)