如何使RxJs代码更加优雅?记录鼠标在特定区域悬停时间的代码

如何使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

我想使用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; //区域外 如果xdivx2 | | ydivy2{ 控制台,注销 }否则{ //在区域内 控制台,登录 总悬浮时间+=间隔; } }; 常数累加器=rx.intervalINTERVAL; AccumerateMer.subscribe=>{ 积累 }; 接收 .fromEventelement,“mousemove” .piperxOp.debounce=>rx.timerINTERVAL .subscribee:MouseEvent=>{ mousePos.x=e.clientX; mousePos.y=e.clientY; }; 我对rxjs不是很熟悉,我认为这段代码可以更优雅地实现

优化代码 非常感谢你的回答@雨果•德鲁•伯尼

const元素=document.body; 常数间隔=2000; const withinBounds={x,y}:{x:number;y:number}=>{ const divx1=element.offsetLeft; const divy1=element.offsetTop; const divx2=element.offsetLeft+element.offsetWidth; const divy2=element.offsetTop+element.offsetHeight; 边界外常数=xdivx2 | | ydivy2; 如果超出边界{ //区域外 控制台。注销; }否则{ //在区域内 控制台。登录; } 返回!边界外; }; 常量鼠标位置=rx .fromEventdocument,“mousemove” .piperxOp.throttleTime200 .piperxOp.mape:MouseEvent=>{x:e.pageX,y:e.pageY}; const mousePositionIsValid=鼠标位置 .piperxOp.mapwithinBounds .piperxOp.distinctuntill更改; const hoverTimer=mousePositionIsValid.piperxOp.switchMapvalid=>valid?累加器:rx.empty; const totalHoverTime=hoverTimer.piperxOp.scanx,y=>x+间隔,-500;//鼠标第一次进入时,会触发一次,因此设置为-500,第一次进入时为0毫秒。 totalHoverTime.Subscribeh超时=>{ log'totalHoverTime为:',hoverTime; };
最后,我发现我仍然需要使用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);