你如何在 rxjs 中组合 Scheduler.animationFrame 和 fromEvent(window, 'scroll') 流?
How do would you combine Scheduler.animationFrame and fromEvent(window, 'scroll') streams in rxjs?
我有两个可观察值 Observable.of(0, animationFrame)
和 Observable.fromEvent(window, 'scroll')
。我想将它们结合起来,以便我的 renderScrollBar(event)
仅在 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++));
}
zip
是一个快速而肮脏的解决方案,它有一个潜在的严重缺陷:当滚动事件发出的速度快于帧速率时,滚动处理程序滞后于真正的滚动值。它看起来像:
Observable.fromEvent(window, 'scroll')
.map(event => event.currentTarget)
.zip(Observable.of(0, animationFrame)
.repeat(),
(currentTarget, _) => this._renderScrollBar(currentTarget)
);
如果您需要 event
包含最新的滚动信息,您可能需要求助于 the window
operator 的更精细的解决方案,例如:
Observable.fromEvent(window, 'scroll')
.map(event => event.currentTarget)
.window(Observable.of(0, animationFrame).repeat())
.mergeMap(w => w.takeLast(1))
.subscribe(currentTarget => this._renderScrollBar(currentTarget));
- 最重要的是,
window
从源创建一个 Observable 块流,每次参数 Observable 发出一个项目时都会分块。在这种情况下,一系列滚动事件由每个动画节拍分块。
- 如果块不为空,我们只从每个块中获取最新的,并且
mergeMap
将其输出到平面输出流。空块不会为这个最终流贡献任何项目。
如果您想限制滚动事件到 requestAnimationFrame,您可以使用 throttleTime(0, animationFrame)
运算符而不是 Observable.of(0, animationFrame)
。
例子
Observable.fromEvent(window, 'scroll')
.throttleTime(0, animationFrame)
更新:
RxJS 6
import { fromEvent, animationFrameScheduler } from "rxjs";
import { throttleTime } from "rxjs/operators";
fromEvent(window, "scroll")
.pipe(throttleTime(0, animationFrameScheduler))
interval(0)
.pipe(throttleTime(0, animationFrameScheduler))
.subscribe(() => {
fifth++;
});
scheduled(interval(0), animationFrameScheduler).subscribe(() => {
sixth++;
});
无论我们将时间设置为 1、2 或 100 秒,这两个似乎只相差 1 帧 ;)
REAL: 181
FIRST: 109
SECOND: 217
THIRD: 108
FOURTH: 217
FIFTH: 180
我有两个可观察值 Observable.of(0, animationFrame)
和 Observable.fromEvent(window, 'scroll')
。我想将它们结合起来,以便我的 renderScrollBar(event)
仅在 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++));
}
zip
是一个快速而肮脏的解决方案,它有一个潜在的严重缺陷:当滚动事件发出的速度快于帧速率时,滚动处理程序滞后于真正的滚动值。它看起来像:
Observable.fromEvent(window, 'scroll')
.map(event => event.currentTarget)
.zip(Observable.of(0, animationFrame)
.repeat(),
(currentTarget, _) => this._renderScrollBar(currentTarget)
);
如果您需要 event
包含最新的滚动信息,您可能需要求助于 the window
operator 的更精细的解决方案,例如:
Observable.fromEvent(window, 'scroll')
.map(event => event.currentTarget)
.window(Observable.of(0, animationFrame).repeat())
.mergeMap(w => w.takeLast(1))
.subscribe(currentTarget => this._renderScrollBar(currentTarget));
- 最重要的是,
window
从源创建一个 Observable 块流,每次参数 Observable 发出一个项目时都会分块。在这种情况下,一系列滚动事件由每个动画节拍分块。 - 如果块不为空,我们只从每个块中获取最新的,并且
mergeMap
将其输出到平面输出流。空块不会为这个最终流贡献任何项目。
如果您想限制滚动事件到 requestAnimationFrame,您可以使用 throttleTime(0, animationFrame)
运算符而不是 Observable.of(0, animationFrame)
。
例子
Observable.fromEvent(window, 'scroll')
.throttleTime(0, animationFrame)
更新:
RxJS 6
import { fromEvent, animationFrameScheduler } from "rxjs";
import { throttleTime } from "rxjs/operators";
fromEvent(window, "scroll")
.pipe(throttleTime(0, animationFrameScheduler))
interval(0)
.pipe(throttleTime(0, animationFrameScheduler))
.subscribe(() => {
fifth++;
});
scheduled(interval(0), animationFrameScheduler).subscribe(() => {
sixth++;
});
无论我们将时间设置为 1、2 或 100 秒,这两个似乎只相差 1 帧 ;)
REAL: 181
FIRST: 109
SECOND: 217
THIRD: 108
FOURTH: 217
FIFTH: 180