我对反应式编程(和RxJS)很陌生,所有这些运算符都很难理解。
无论如何,我已经成功编写了这个函数,可以在拖动文件的同时处理文档的滚动。我现在想知道这是否可以简化。
基本上,onMouseDown我需要每隔10ms检查一次鼠标的位置,当鼠标移动时我需要更新clientY
,这就是为什么我设置了{mouse}观察者组合的Rx.Oberservable.interval(10)
。这将滚动页面,无论您是否移动鼠标(按预期)。
以下是代码:
handleWindowScrollOnDrag() {
var dragTarget = this.getDOMNode()
var scrollTarget = document.body
var wHeight = window.innerHeight
var maxScroll = document.documentElement.scrollHeight - wHeight
// Get the three major events
var mouseup = Rx.Observable.fromEvent(document, 'mouseup');
var mousemove = Rx.Observable.fromEvent(document, 'mousemove');
var mousedown = Rx.Observable.fromEvent(dragTarget, 'mousedown');
var mousedrag = mousedown.flatMap(function (md) {
var y = scrollTarget.scrollTop
var multiplier = 1
// Scroll every 10ms until mouseup when we can
var intervalSource = Rx.Observable.interval(10).takeUntil(mouseup);
// Get actual clientY until mouseup
var movement = mousemove.map(function (mm) {
return {
y: mm.clientY
};
}).takeUntil(mouseup);
return Rx.Observable
.combineLatest(movement, intervalSource, function (s1) {
multiplier = 1
if (s1.y < 100 && y >= 0) {
if (s1.y < 75) multiplier = 3;
if (s1.y < 50) multiplier = 5;
if (s1.y < 25) multiplier = 10;
if (s1.y < 15) multiplier = 20;
y -= (1 * multiplier)
}
if (s1.y > wHeight - 100 && y <= (maxScroll)) {
if (s1.y > wHeight - 75) multiplier = 3;
if (s1.y > wHeight - 50) multiplier = 5;
if (s1.y > wHeight - 25) multiplier = 10;
if (s1.y > wHeight - 15) multiplier = 20;
y += (1 * multiplier)
}
return {
y: y
};
});
});
// Update position
this.subscription = mousedrag.subscribe(function (pos) {
document.body.scrollTop = pos.y
});
},
答案 0 :(得分:3)
您可以删除takeUntil
流上只需要一个movement
的额外combineLatest
,因为.scan
运算符将在完成时清理并处理所有基础订阅。
接下来,您可以使用y
来管理累积状态而不是闭包变量,从而删除一些额外状态。
您还可以通过简单地传递鼠标移动map
值来简化事件正文,而不是在combineLatest
和.withLatestFrom
中产生对象分配的开销运算符。
最后,我会更改为使用combineLatest
而不是combineLatest
。由于您基本上已经以100 fps进行轮询,间隔为jQuery.animate
,如果鼠标同时移动,则会发出更快的速度。
作为旁注,虽然我对DOM滚动的工作方式并不十分熟悉(而且我实际上并不知道您的代码在野外的效果如何),但垃圾邮件的页面滚动值比页面的实际渲染率似乎有点过分。降低间隔率可能会更好,并使用类似handleWindowScrollOnDrag() {
var dragTarget = this.getDOMNode()
var scrollTarget = document.body;
var wHeight = window.innerHeight;
var maxScroll = document.documentElement.scrollHeight - wHeight;
// Get the three major events
var mouseup = Rx.Observable.fromEvent(document, 'mouseup');
var mousemove = Rx.Observable.fromEvent(document, 'mousemove');
var mousedown = Rx.Observable.fromEvent(dragTarget, 'mousedown');
var mousedrag = mousedown.flatMap(function (md) {
// Scroll every 10ms until mouseup when we can
var intervalSource = Rx.Observable.interval(10, Rx.Scheduler.requestAnimationFrame);
return intervalSource
.takeUntil(mouseup)
.withLatestFrom(mousemove, function (s1, s2) {
return s2.clientY;
})
.scan(scrollTarget.scrollTop, function(y, delta) {
var multiplier = 1;
if (delta < 100 && y >= 0) {
if (delta < 75) multiplier = 3;
if (delta < 50) multiplier = 5;
if (delta < 25) multiplier = 10;
if (delta < 15) multiplier = 20;
y -= (1 * multiplier);
}
if (delta > wHeight - 100 && y <= (maxScroll)) {
if (delta > wHeight - 75) multiplier = 3;
if (delta > wHeight - 50) multiplier = 5;
if (delta > wHeight - 25) multiplier = 10;
if (delta > wHeight - 15) multiplier = 20;
y += (1 * multiplier);
}
return y;
});
});
// Update position
this.subscription = mousedrag.subscribe(function (pos) {
document.body.scrollTop = pos;
});
},
的内容来平滑其间的滚动。
; tldr
movement
修改强>
原版中的错误实际上允许人们进一步简化代码。您可以通过将map
传递给mousemove
并使用该结果选择器获取{{1},将所有withLatestFrom
流一起移除(并通过额外调用clientY
) }}
此外,如果您尝试将interval
与页面的渲染循环同步,我会添加您可能想要添加调度程序的位置,但我仍然会说您可能希望使用15ms(60fps)而不是10ms,它再次归结为滚动渲染的完成方式。
如果设置滚动位置只会在每个渲染循环结束时更新,那么它将正常工作。但是,如果它在每set
之后贪婪地重新计算,那么最好让像React
这样的中间件首先写入Virtual DOM而不是直接将所有更改刷新到DOM。