Javascript 滚动事件:requestAnimationFrame VS requestIdleCallback VS被动事件侦听器

Javascript 滚动事件:requestAnimationFrame VS requestIdleCallback VS被动事件侦听器,javascript,dom,requestanimationframe,requestidlecallback,passive-event-listeners,Javascript,Dom,Requestanimationframe,Requestidlecallback,Passive Event Listeners,正如我们所知,通常建议对滚动侦听器进行去抖动处理,以便用户在滚动时更好地使用用户体验 然而,我经常发现像Paul Lewis这样有影响力的人推荐使用requestAnimationFrame。然而,随着web平台的快速发展,随着时间的推移,一些建议可能会被弃用 我看到的问题是,对于处理滚动事件,有非常不同的用例,比如构建视差网站,或者处理无限滚动和分页 我看到3个主要工具可以在用户体验方面发挥作用: 所以,我想知道,每个用例(我只有2个,但你可以想出其他的),我现在应该使用什么样的工具

正如我们所知,通常建议对滚动侦听器进行去抖动处理,以便用户在滚动时更好地使用用户体验

然而,我经常发现像Paul Lewis这样有影响力的人推荐使用
requestAnimationFrame
。然而,随着web平台的快速发展,随着时间的推移,一些建议可能会被弃用

我看到的问题是,对于处理滚动事件,有非常不同的用例,比如构建视差网站,或者处理无限滚动和分页

我看到3个主要工具可以在用户体验方面发挥作用:

所以,我想知道,每个用例(我只有2个,但你可以想出其他的),我现在应该使用什么样的工具来获得非常好的滚动体验


更准确地说,我的主要问题是与无限滚动视图和分页(通常不必触发视觉动画,但我们希望获得良好的滚动体验)相关的,是否最好将
requestAnimationFrame
替换为
requestIdleCallback
+被动滚动事件处理程序的组合?我还想知道使用
requestIdleCallback
调用API或处理API响应以让滚动更好地执行,或者是浏览器可能已经为我们处理了什么?尽管这个问题有点老,但我想回答它,因为我经常看到脚本,其中许多技术被滥用

一般来说,你需要的所有工具(
rAF
rIC
和被动侦听器)都是很棒的工具,不会很快消失。但你必须知道为什么要使用它们

在我开始之前:如果生成滚动同步/滚动链接效果,如视差效果/粘性元素,使用
rIC
进行节流,
setTimeout
没有意义,因为您希望立即做出反应

requestAnimationFrame
rAF
在浏览器想要计算文档的新样式和布局之前,为您提供框架生命周期内的点。这就是为什么它是完美的动画使用。首先,调用它的频率不会比浏览器计算布局的频率高或低(正确的频率)。其次,在浏览器计算布局之前调用它(正确计时)。事实上,使用
rAF
进行任何布局更改(DOM或CSSOM更改)都很有意义<代码>rAF与浏览器中与布局渲染相关的任何其他内容同步

使用
rAF
进行油门/去盎司 Paul Lewis的默认示例如下所示:

var scheduledAnimationFrame;
function readAndUpdatePage(){
  console.log('read and update');
  scheduledAnimationFrame = false;
}

function onScroll (evt) {

  // Store the scroll value for laterz.
  lastScrollY = window.scrollY;

  // Prevent multiple rAF callbacks.
  if (scheduledAnimationFrame){
    return;
  }

  scheduledAnimationFrame = true;
  requestAnimationFrame(readAndUpdatePage);
}

window.addEventListener('scroll', onScroll);
//declare box, element, pos
function writeLayout(){
    element.classList.add('is-foo');
}

window.addEventListener('scroll', ()=> {
    box = element.getBoundingClientRect();

    if(box.top > pos){
        requestAnimationFrame(writeLayout);
    }
});
这种模式经常被使用/复制,尽管在实践中几乎毫无意义。(我在问自己,为什么没有开发人员看到这个明显的问题。)一般来说,理论上,将所有内容限制到至少
rAF
,是很有意义的,因为从浏览器请求布局更改的频率比浏览器渲染布局的频率更高是没有意义的

但是,每次浏览器呈现滚动位置更改时,都会触发滚动事件。这意味着
滚动
事件与页面呈现同步。与英国皇家空军给你的东西完全一样。这意味着用某个东西来限制某个东西没有任何意义,根据定义,这个东西已经被完全相同的东西限制了

实际上,您可以通过添加
console.log
来检查我刚才所说的,并检查此模式“防止多个rAF回调”的频率(答案是“无”,否则将是浏览器错误)

正如您将看到的,这段代码从未执行过,它只是死代码

但有一个非常相似的模式,因为不同的原因而有意义。看起来是这样的:

var scheduledAnimationFrame;
function readAndUpdatePage(){
  console.log('read and update');
  scheduledAnimationFrame = false;
}

function onScroll (evt) {

  // Store the scroll value for laterz.
  lastScrollY = window.scrollY;

  // Prevent multiple rAF callbacks.
  if (scheduledAnimationFrame){
    return;
  }

  scheduledAnimationFrame = true;
  requestAnimationFrame(readAndUpdatePage);
}

window.addEventListener('scroll', onScroll);
//declare box, element, pos
function writeLayout(){
    element.classList.add('is-foo');
}

window.addEventListener('scroll', ()=> {
    box = element.getBoundingClientRect();

    if(box.top > pos){
        requestAnimationFrame(writeLayout);
    }
});
使用此模式,您可以成功地减少甚至消除布局抖动。想法很简单:在滚动侦听器中,您阅读布局并决定是否需要修改DOM,然后使用rAF调用修改DOM的函数。这为什么有用?
rAF
确保您移动布局无效(在框架末端)。这意味着在同一框架内调用的任何其他代码都可以在有效布局上工作,并且可以使用超快速布局读取方法进行操作

这个模式实际上非常好,因此我建议使用以下helper方法(用ES5编写):

requestIdleCallback
源于API,类似于rAF,但给出了完全不同的东西。它为您提供帧内的一些空闲时间。(通常是浏览器计算布局并完成绘制后的点,但距离v同步还有一段时间。)即使页面在用户视图中滞后,也可能存在一些帧,其中浏览器处于空闲状态。虽然
rIC
可以为您提供最长50毫秒。大多数情况下,您只有0.5到10毫秒的时间来完成任务。由于调用帧生命周期
rIC
回调的时间点,因此不应更改DOM(为此使用
rAF

最后,使用
rIC
scroll
监听器进行节流以实现懒散加载、无限滚动等功能是非常有意义的。对于这些类型的用户界面,您甚至可以在其前面添加一个
setTimeout
。(因此您需要等待100毫秒,然后执行
rIC

和的实例。)

这里是,它包括两个图表,这可能有助于理解“框架生命周期”中的不同点

被动事件侦听器 被动事件侦听器的发明是为了提高滚动性能。现代浏览器将页面滚动(滚动呈现)从主线程移动到合成线程。(见附件)

但是他们