如何使RxJs代码更加优雅?记录鼠标在特定区域悬停时间的代码
我想使用RxJs记录鼠标在特定区域(例如“div”容器框区域)上的悬停时间 const元素=document.querySelector'some-div'; 设totalHoverTime=0; 常数间隔=100; 常数鼠标塞={ x:-1, y:-1 }; 常数累加==>{ 常数x=鼠标点x; 常数y=鼠标点y; const divx1=element.offsetLeft; const divy1=element.offsetTop; const divx2=element.offsetLeft+element.offsetWidth; const divy2=element.offsetTop+element.offsetHeight; //区域外 如果x如何使RxJs代码更加优雅?记录鼠标在特定区域悬停时间的代码,rxjs,Rxjs,我想使用RxJs记录鼠标在特定区域(例如“div”容器框区域)上的悬停时间 const元素=document.querySelector'some-div'; 设totalHoverTime=0; 常数间隔=100; 常数鼠标塞={ x:-1, y:-1 }; 常数累加==>{ 常数x=鼠标点x; 常数y=鼠标点y; const divx1=element.offsetLeft; const divy1=element.offsetTop; const divx2=element.offsetL
最后,我发现我仍然需要使用mousemove事件组合计时器来实现这个功能。当鼠标在页面加载时已经悬停在div上方时,mouseenter事件将永远不会在我的页面中触发。也许只有在中可能没有问题。我最近才开始使用RxJS,所以可能有更好的方法来解决您的问题 然而,与您的方法相比,一个巨大的改进已经是链接可观察对象并使用switchMap操作符。使用rxjs时需要记住的一件事是,您希望避免手动订阅,因为您必须跟踪这些订阅并取消订阅以防止泄漏。当使用像switchMap这样的操作符时,它们会跟踪对内部可观察对象的订阅,并自动取消订阅 以下代码片段应该可以解决您的问题:
Rx.Observable.fromEvent(element, 'mouseenter') // returns Observable<Event>
.map(() => Date.now()) // transform to Observable<number>
.switchMap((startTime) => { // switches to new inner observable
return Rx.Observable.fromEvent(button, 'mouseleave')
// When the observable from mouseleave emmits, calculate the hover time
.map(() => Date.now() - startTime);
})
.subscribe((hoverTime) => {console.log(hoverTime)});
至少在fiddle中,当鼠标在页面加载时已悬停在div上方时,此功能也会起作用,因为此时也会触发mouseenter事件。Point free
最简单的方法是:用简单的f替换x=>fx。它是等效的,在大多数情况下会读得更好。这:
accumulateTimer.subscribe(() => {
accumulate();
});
变成:
accumulateTimer.subscribe(accumulate);
脂肪功能:
累积功能可分为:
const accumulate = () => {
const x = mousePos.x;
const y = mousePos.y;
if (withinBounds(x, y)) {
totalHoverTime += INTERVAL;
}
};
const withinBounds = ({x, y}) => {
const divx1 = element.offsetLeft;
const divy1 = element.offsetTop;
const divx2 = element.offsetLeft + element.offsetWidth;
const divy2 = element.offsetTop + element.offsetHeight;
const outOfBounds = x < divx1 || x > divx2 || y < divy1 || y > divy2;
if (outOfBounds) {
// out of area
console.log('out')
} else {
// in area
console.log('in')
}
return !outOfBounds;
};
不要订阅并保存价值,这打破了rxjs背后的流程观念。使用返回值,Luke。更具体地说,通过管道进一步细化,直到达到所需的数据。上面,我们有一个单独发射鼠标位置的流
// Will emit true when the mouse enters and false when it leaves:
const mousePositionIsValid = mousePositions
.map(withinBounds)
.distinctUntilChanged();
// Fires every INTERVAL, only when mouse is within bounds:
const hoverTimer = mousePositionIsValid
.switchMap(valid => valid ? accumulateTimer : rx.empty())
根据@der_berni的建议,用编辑
您编写了一个名为accumulate的函数。每当你说“积累”的时候,我就会想起你喜欢的东西。Reduce在流完成时发出一个聚合值。在这里,每次底层流发射时,我们都会获得一个新的更新值:
// For each element produced by hoverTimer, add INTERVAL
const totalHoverTime = hoverTimer.scan((x, y) => x + INTERVAL, 0);
请注意,它不会每次都添加到全局,但它发出的每个值都是前一个值+间隔。所以你可以订阅它来获得你的总悬停时间。太好了!但“mouseenter”和“mouseleave”事件存在问题。如果我的目标元素非常大,可以覆盖整个页面,那么鼠标总是在该元素上,所以mouseenter事件永远不会被调度。这就是为什么我使用“mousemove”事件来实现它。原谅我糟糕的英语:实际上,这也可以用rxjs解决;见我的编辑上面
// Will emit true when the mouse enters and false when it leaves:
const mousePositionIsValid = mousePositions
.map(withinBounds)
.distinctUntilChanged();
// Fires every INTERVAL, only when mouse is within bounds:
const hoverTimer = mousePositionIsValid
.switchMap(valid => valid ? accumulateTimer : rx.empty())
// For each element produced by hoverTimer, add INTERVAL
const totalHoverTime = hoverTimer.scan((x, y) => x + INTERVAL, 0);