简体   繁体   中英

How do would you combine Scheduler.animationFrame and fromEvent(window, 'scroll') streams in rxjs?

I have two observables Observable.of(0, animationFrame) & Observable.fromEvent(window, 'scroll') . I want to combine them so that my renderScrollBar(event) is only called on the ticks of animationFrame.

  Observable.fromEvent(window, 'scroll')
      .map((event: any) => event.currentTarget)
      .subscribe((event) => {
        this._renderScrollBar(event);
      });
  let x = 0;
  Observable.of(0, animationFrame)
      .repeat()
      .takeUntil(Observable.timer(1000))
      .subscribe(() => console.log(x++));
}

If you want to throttle scroll event to requestAnimationFrame you can use throttleTime(0, animationFrame) operator instead of Observable.of(0, animationFrame) .

Example

Observable.fromEvent(window, 'scroll')
    .throttleTime(0, animationFrame)

Update:

RxJS 6

import { fromEvent, animationFrameScheduler } from "rxjs";
import { throttleTime } from "rxjs/operators";

fromEvent(window, "scroll")
  .pipe(throttleTime(0, animationFrameScheduler))

zip is a quick and dirty solution that has a potentially serious flaw: the scroll handler lags behind the true scroll value when scroll events emit faster than the frame rate. It would look something like:

Observable.fromEvent(window, 'scroll')
          .map(event => event.currentTarget)
          .zip(Observable.of(0, animationFrame)
                         .repeat(),
            (currentTarget, _) => this._renderScrollBar(currentTarget)
          );

If you need event to contain the very latest scroll information, you might need to resort to a more elaborate solution with the window operator , like:

Observable.fromEvent(window, 'scroll')
          .map(event => event.currentTarget)
          .window(Observable.of(0, animationFrame).repeat())
          .mergeMap(w => w.takeLast(1))
          .subscribe(currentTarget => this._renderScrollBar(currentTarget));
  1. Foremost, window creates a stream of Observable chunks from the source, chunked each time the argument Observable emits an item. In this case, series of scroll events are chunked by each animation tick.
  2. We take only the latest from each chunk if the chunk isn't empty, and mergeMap that to a flat output stream. Empty chunks won't contribute any items to this final stream.
interval(0)
  .pipe(throttleTime(0, animationFrameScheduler))
  .subscribe(() => {
    fifth++;
  });

scheduled(interval(0), animationFrameScheduler).subscribe(() => {
  sixth++;
});

those two seems to be just 1 frame off whatever we set time for 1, 2 or 100 seconds ;)

REAL:  181
FIRST:  109
SECOND:  217
THIRD:  108
FOURTH:  217
FIFTH:  180

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM