jQuery mousemove性能 - 减少事件频率?

8

我们在处理与mousemove相关的jQuery事件传播性能方面遇到了问题:

我们有一个覆盖整个屏幕的画布,并需要跟踪用户是否在其上拖动鼠标,因此我们在该对象上添加了一个mousemove监听器,如下所示:

ourCanvas.on('mousemove',
   function(event) {
      event.preventDefault();
      //our drag code here
   }
});

这段代码本来可以正常工作,但是在一个测试系统上,我们发现当前Firefox(24版本)存在严重的性能问题。分析器告诉我们,大部分时间都花费在jQuery.event.dispatch()函数上(我们尝试了目前最新的jQuery 1.8、1.9、1.10和2.0版本)。

通过使用“jQuery.event.fix()”性能优化,我们成功地减少了在dispatch()函数中所花费的时间。具体可以参考这里:http://bitovi.com/blog/2012/04/faster-jquery-event-fix.html,但是在那个测试系统上的表现仍然远远低于我们的预期。

经过进一步的测试,我成功地将问题定位在了系统上使用的鼠标:它的频率为1000Hz。我们将使用的鼠标改为125Hz,结果表现出色。

我们的假设是,鼠标高的Hz频率会导致产生大量的mousemove事件,因此我们改变了上述代码,应用了事件节流,并且只在每X毫秒调用一次事件处理程序:

var lastMove = 0;
var eventThrottle = 1;
ourCanvas.on('mousemove',
   function(event) {
      event.preventDefault();
      var now = Date.now();
      if (now > lastMove + eventThrottle) {
          lastMove = now;
          //our drag code here
      }
   }
});

它像魔术一样奏效,性能很棒。尽管我们只跳过了两毫秒的事件。

现在我有两个问题:

  1. 我们还有其他位置,在不同的HTML元素上附加mousemove监听器,并且我想将这个自制的限流器添加到所有这些mousemove处理程序中,以避免再次遇到此问题。在jQuery(2.0.3)中,是否有一种好的方式可以实现这一点?我已经看到了jQuery javascript中的preDispatch钩子,但它们已经在调用fix()之后使用了一些时间,我也想节省那个调用。

  2. 我对一个2ms的eventThrottle就足以获得非常好的性能感到困惑,所以我添加了一个计数器来查看有多少事件被跳过了。令人惊讶的结果是:它只跳过了0-1个事件...当节流为100ms时,跳过的事件大约是60-70个,那么如果每毫秒少于1个mousemove事件,为什么这段代码仍然具有如此积极的作用呢?

谢谢任何评论, 克里斯托弗

2个回答

4
2015年末,我遇到了一个问题。在我的浏览器应用程序中,我会在特定位置绘制多个不同大小的圆圈,然后拖动屏幕以显示当前缩放级别下可见的圆圈。当鼠标被拖动时,会触发mousemove事件,这会调用我的渲染程序并导致每个可见圆圈进行重新绘制。在IE 11中测试时,我发现一旦视野区域内有100多个圆圈时,拖动鼠标时的渲染变得极为卡顿。分析器表明这几乎完全是由于paint()例程引起的。我的代码已经使用一个库的requestAnimationFrame()。有趣的是,在拖动屏幕时,我看到了减速运动;但是如果我只是拖动屏幕并释放它,让库代码继续使用减速度动画,重新绘制就像一块黄油一样顺畅。这种减速运动仅在拖动鼠标时出现卡顿。问题显然与mousemove有关。(稍后再回到这个问题)。
我将paint()例程简化为仅仅是一个填充弧线——同样的问题。我尝试在改变缩放级别时将填充圆绘制到一个离屏画布上,然后使用drawImage()将离屏画布复制到我的主屏幕上——这提高了性能,但在IE中仍然无法使用。我随后尝试使用这种技术将所有圆绘制到与主可见窗口大小相同的离屏画布上,然后将paint()更改为仅仅将离屏画布复制到可见画布上——这再次带来了一些改进,但不够多。
后来我尝试在各种浏览器中运行我的应用程序:IE 11, Firefox 42:非常卡顿;Chrome 47,Opera 34:在所有缩放级别下都非常流畅;桌面版Safari 5.1.7(在PC上):在所有缩放级别下都稍微卡顿。问题显然与mousemove和它在不同浏览器中的处理有关。
最终,我发现了StackOverflow上的这个问题以及它的建议:鼠标本身发送了如此多的mousemove事件,以至于它淹没了浏览器快速重新绘制的能力。而我确实有一个生成速度很高的现代鼠标。我尝试添加eventThrottle检查到我的mousemove事件处理程序中,哇!成功了。我的代码现在在所有浏览器上都可以平稳地渲染。(愉快地点了赞)。
我希望为那些可能遇到高频率鼠标拖动时在IE和Firefox中出现paint()性能差的问题的人们提供这些额外的信息。减少mousemove事件处理次数的建议对我奏效了。

2

1 - 有一个名为“jQuery Throttle”插件:https://github.com/cowboy/jquery-throttle-debounce

正如您可以在示例中所读到的那样,您可以替换:

// Bind the not-at-all throttled handler to the resize event.
$(window).resize( handler );

// Bind the throttled handler to the resize event.
$(window).resize( $.throttle( 250, handler ) ); // This is the line you want!

2 - 你想发布你的处理程序代码吗?

一个盲目的建议:Firebug在FF 24中存在性能问题。你尝试过启用/禁用Firebug来比较性能吗?


谢谢:@1:有趣的插件使用setTimeout()clearTimeout(),但它并不限制实际事件处理。两个副作用:1:我仍然需要调用$.throttle()而不是$.on()(在多个开发人员中难以实现)。2:CPU重的jQuery处理开销仍然存在(调用fix()等)。也许最干净的解决方案是自己覆盖jQuery事件处理函数作为“插件”。@2我们的处理程序更改从运行在requestAnimationFrame上的循环中访问的一些变量。@Firebug:您对FF24的看法是正确的,只有在禁用Firebug的情况下才进行了测量。 - Christopher Lörken
如果您想了解是什么占用了您的CPU,您需要发布一些代码以获得帮助... - LeGEC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接