JavaScript OnScroll性能比较

17

更新:类似的问题已有非常好的答案,展示了如何以有用的方式使用requestAnimationFrame处理滚动:scroll events: requestAnimationFrame VS requestIdleCallback VS passive event listeners


假设我想通过滚动来触发网站上的一些昂贵的操作。例如,我在我的jsfiddle中使用视差效果。

现在我不停地看到不能直接绑定事件,有时会跟随着更好的代码片段。以下是一些例子:

  1. 将JavaScript处理程序附加到滚动事件= BAD!
  2. 如何开发高性能onScroll事件?
  3. 如何使滚动效果更快?
  4. 60FPS onscroll 事件监听器

他们的意思基本上是不要这样做:

  // Bad guy 1
  $(window).scroll( function() {
    animate(ex1);
  });

或者这个

  // Bad guy 2
  window.addEventListener('scroll', onScroll, false);
  function onScroll() {
    animate(ex2);
  }

但是使用超时,间隔,requestAnimationFrame等方法,例如:

  // Good guy
  $(window).scroll( function() {
   scrolling1 = true;
  });

  setInterval( function() {
    if (scrolling1) {
      scrolling1 = false;
      animate(ex3);
    }
  }, 50 );

所以,我去了解了上面链接中找到的选项,并将它们添加到一个jsfiddle中,试图通过向每种方法添加计数器来比较它们,如下所示:

  // Test
  $(window).scroll( function() {
    counter = counter + 1;
    // output result of counter
    animate(ex1);
  });

建议检查完整的jsfiddle

结果:一切顺利的操作大致需要相同数量的计算资源。如果我可以接受不太流畅的效果,也许我可以节省一些资源。尽管我读到的所有信息都与此相反,但这对我来说似乎很合理!

第一个问题:我有漏掉的地方吗?或者这是个有效的测试吗?如果无效,应该如何正确测试? 编辑:澄清一下,我想测试是否有任何上述方法可以提高性能。

第二个问题:如果它是有效的,为什么每个人都会对onscroll感到紧张?如果流体动画需要在整个站点上进行5000次计算,那么无论如何都无法改变吗?

(嗯,有时候我使用检查来确定对象是否在视口中,但老实说,我甚至不知道这些检查是否和本身的代码一样昂贵,尤其是如果它们涉及五个不同的变量,如offset、windowHeight、scrollTop、getBoundingClientRect和outerHeight...)


1
你在测试什么?性能吗?独立于浏览器(JavaScript引擎)、设备和操作系统?你需要测试所有这些东西……但无论如何,我认为你应该使用被动事件监听器或其他东西来处理滚动事件。 - Cody G
1
看起来现在Chrome和Firefox已经自动处理了touchstarttouchend的滚动性能问题... - Cody G
2
最后,https://developer.mozilla.org/en-US/docs/Web/Events/scroll - Cody G
谢谢Cody。我正在尝试验证上述任何方法是否实际优化了性能,或者是否是不必要的。我已将此添加到我的问题中。我很快会仔细阅读这些链接。(看起来我应该在旧浏览器中进行测试,以查看不同方法之间的更多差异?) - user127091
我现在已经阅读了上面的链接。发现:A)根据 Mozilla 的说法,事件监听器在 Firefox 和 Chrome 中现在默认情况下是被动的。B)Mozilla 自己说,在这种情况下请求动画帧是无用的,因为它以相同的速度触发(根据我的测试是正确的)。这就留下了间隔和超时选项,如果不将它们设置为几乎与它本来会触发的速度相同,则会很不流畅。 - user127091
显示剩余2条评论
2个回答

12

所以,@SirPeople已经正确回答了你的第一个问题,这确实是一个很好的测试来查看animate函数被调用的频率,但它不适合比较不同代码片段的性能。

这是执行的性能记录:

performance test

函数animate非常不昂贵。 我进行了性能记录(下一张图片),显示它在我查看的一个迭代中花费了0.64ms至1.29ms之间的时间(点1-5)。 一旦函数完成,重绘根本不需要时间(点6),这可能是因为页面几乎没有内容。 当我们查看时间时,可以看到所有五个动画函数和重新绘制发生在不到10ms的时间内,这在正常情况下意味着我们可以获得流畅的60fps动画(点7)。
此外,如果我们想比较onscroll事件侦听器,我们需要分别测试每个侦听器并比较结果。 如果其中一个侦听器确实会阻塞,它会对整个页面产生影响,而没有性能调试,您就无法知道是哪一个。
我制作了两个jsfiddleswindow.scrollRAF。 令我惊讶的是,似乎没有任何区别。
人们为什么担心这个问题?
正如您在上面链接的jsfiddles中所看到的,如果事件处理程序太大,则整个页面将会出现延迟。
现在怎么办?
我本人不是性能达人,但是:

1
好的,谢谢。我没有尝试让animate函数变得更昂贵,将方法在不同的网站上进行比较也更合理。我想你的答案、Cody的评论和@SirPeople的答案已经说得差不多了。让我们看看Intersection Observer是否会在未来改变事情。 - user127091
嘿,非常好的回答,但不幸的是那个示例现在出现404错误了。- 有人有类似的例子吗?我正在努力理解这个主题。 - user3674592
同时,MDN 表示: “对于基本的滚动事件,您不需要担心被动值。由于它无法被取消,事件监听器无论如何都不能阻止页面渲染。”因此,滚动事件默认启用被动模式,因此不会有任何影响? - user3674592

9

我不完全确定我是否正确理解了你的问题和所有陈述,但我会尝试给出一个答案:

  • 我是否遗漏了什么,或这是一个有效的测试?如果无效,我该如何进行正确的测试?

如果您正在测量函数被调用的次数,则这是一个有效的测试,当然这将取决于浏览器、SO、是否启用GPU以及已在您的问题中注释的某些基准参数。

如果我们认为这个测量值是正确的,那么可以说通过使用超时或requestAnimationFramework 可能 节省时间,因为我们基本上遵循了去抖动或节流 的原则。基本上,我们不想请求或调用比所需更多的功能。在计时器的情况下,我们将排队较少的函数调用,在requestAnimationFrame的情况下,由于它在重新绘制之前将调用入队并按顺序执行它们。在超时中,如果它们非常重,可能会发生重叠计算。

我在这里找到了一个更好的答案关于为什么使用requestAnimationFrame,解释了浏览器中动画的主要问题,如剪切、闪烁或帧跳过。它还包括一个很好的演示。

我认为您的测试方法是正确的,您还应该正确地解释它,也许由于您的硬件和引擎而调用的次数接近相同,但正如所说,去抖动和节流是性能缓解。

这里还有一篇支持不将处理程序附加到窗口滚动事件的Twitter文章(免责声明:这篇文章来自2011年,浏览器以不同的方式进行了优化)。

  • 为什么每个人都对onscroll感到紧张?如果流体动画需要完整网站上的5000个计算,无论如何都无法改变吗?

我不认为在性能影响方面存在紧张感,但是过度调用滚动可能会导致上述动画问题的用户体验更差,即使与您的计时器存在不同步,您仍然可能遇到相同的“性能”问题。人类视觉的持续性不需要超高帧率,因此试图更频繁地显示图像是无用的。对于更复杂的计算或重型动画,浏览器已经在进行优化,就像您所检查的那样,一些浏览器已经对这些问题进行了优化,与您提出的文章撰写2、3或6年前相比。


非常感谢您的帮助,这对我很有帮助。尽管如此,我现在仍然会将Sandros的答案标记为正确答案,因为它指出了问题的起点(昂贵的动画函数)和性能记录,并展示了更好的比较方法(不在一个页面上运行所有示例)。不过,您的回答也很有帮助,再次感谢! - user127091
嘿,很高兴你觉得这个答案有用。关于答案没问题,我也喜欢他对浏览器的分析。 - SirPeople

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