仅在最近两秒内未发射另一个可观察物时才从可观察物发射

时间:2020-03-24 09:56:40

标签: javascript angular animation rxjs

注意:类似的问题是here,但似乎没有明显的答案可以应用。

问题:我有一个“转到顶部”按钮,其功能是将用户滚动到页面顶部。当用户实际上位于顶部时,它应该不可见。

目前,它的可见性是通过不透明度动画属性处理的,但是由于显示的原因,它可以与之交互:未应用任何不可见的属性。因此,我必须找到一种显示的方式:不透明度为0时不适用。

在Angular中,第一个实现是执行以下操作:

    @HostListener('window:scroll', ['$event'])
    scrolled(_event: Event) {
        if (window.pageYOffset === 0) {
            setTimeout(() => (this.noClick = true), 1000);
        } else {
            this.noClick = false;
        }

        this.show = window.pageYOffset > 0;
    }

其中noClickshow是布尔值,并且noClick应用于设置display:none的类,而show应用于动画不透明度。

但是有一种竞争条件,用户可以在滚动到0后向下滚动,并且延迟仍然可以将noClick设置为true。

我想考虑使用Observable解决此问题。

如果我有两个主题:

displayNone = new Subject<boolean>();   // only contains true values
displaySome = new Subject<boolean>();   // only contains false values

和一些事件代码:

    @HostListener('window:scroll', ['$event'])
    scrolled(_event: Event) {
        if (window.pageYOffset === 0) {
            this.displayNone.next(true);
        } else {
            this.displaySome.next(false);
        }

        this.show = window.pageYOffset > 0;
    }

我需要为displayNone功能构建一个可观察对象,其中如果displayNone被延迟1000ms(动画时间),并且在这段时间内displaySome发出,它必须忽略displayNone

我目前能找到的最接近的东西是这样的:

race(this.displaySome, this.displayNone.pipe(delay(1000)))

但是我的理解是,只需要评估一次就可以决定要通过哪一个。我需要不断对其进行评估。

1 个答案:

答案 0 :(得分:0)

您不需要两个Observable即可显示/隐藏带有动画的按钮。最好的解决方案与问题的答案不同。我想同时回答这两个问题。

仅在最近X毫秒内未发出另一个Observable时发出

const X = 1000;
const obs$ = source$.pipe(
  // emit values on a new inner Observable when the otherObservable$ emits
  window(otherObservable$),
  // skip all values on this inner Observable until a given time has passed
  // (but not for the first inner Observable created before otherObservable$ emits)
  mergeMap((w, i) => i == 0 ? w : w.pipe(skipUntil(timer(X))))
);

根据滚动事件显示/隐藏按钮

  1. 从滚动事件中创建一个Observable并映射到一个布尔值,指示是显示还是隐藏按钮。
hasScrolledDown$: Observable<boolean>;

ngOnInit() {
  this.hasScrolledDown$ = fromEvent(window, 'scroll').pipe(
    throttleTime(20),
    map(() => window.pageYOffset > 50)
  )
}
  1. 使用ngIf来显示/隐藏按钮,具体取决于Observable中的值。
<button *ngIf="hasScrolledDown$ | async">
  1. (可选)enterleave状态更改添加动画。
<button *ngIf="hasScrolledDown$ | async" [@fadeAnimation]>
export const fadeOutAnimation = animation([
    style({ opacity: 1 }),
    animate('200ms', style({ opacity: 0 }))
])

export const fadeInAnimation = animation([
    style({ opacity: 0 }),
    animate('200ms', style({ opacity: 1 }))
])

@Component({
  selector: 'button-overview-example',
  templateUrl: 'button-overview-example.html',
  styleUrls: ['button-overview-example.css'],
  animations: [
    trigger('fadeAnimation', [
      transition(':enter', [useAnimation(fadeInAnimation)]),
      transition(':leave', [useAnimation(fadeOutAnimation)])
    ])
  ],
})

Demo