关于丢帧和 requestAnimationFrame 的困惑

Posted

技术标签:

【中文标题】关于丢帧和 requestAnimationFrame 的困惑【英文标题】:Confusion about frame drops and requestAnimationFrame 【发布时间】:2021-09-06 18:34:04 【问题描述】:

如果我的以下理解有误,请随时指出:假设我们的显示刷新率为 60hz(我知道并非总是如此,但我们假设它是 60hz)所以网页会刷新屏幕 60 次如果一切顺利,每一秒。这意味着渲染以 16 毫秒的间隔(大约)发生,对吗?因此,我们的 javascript 中执行时间超过 16 毫秒的任何内容都会给用户带来不便的体验。所以我的问题是:

    假设我们有一个函数handleScroll,从开始到结束执行需要100ms。我们将其添加到addEventListener('scroll', handleScroll)。每当scroll 事件触发时,用户是否会遇到卡顿体验,因为在渲染周期中跳过/丢弃了 6 帧?因为 100ms / 16ms = 6.25?我知道一个任务在主线程上需要很长时间,它会停止所有其他任务直到它完成,但在这里我想获得一些定量分析或定性分析方法来解决这样的性能问题。具体来说,我想了解(大致)这样的回调会丢弃多少帧(如果刷新率为 60hz) 我认为requestAnimationFrame 告诉浏览器在渲染下一帧之前运行回调,所以我看到有人提到它可以防止帧被丢弃以进行动画处理。但我不清楚这将如何提供帮助,因为我们传递给requestAnimationFrame 的回调仍将运行到完成,所以如果该回调花费超过 16 毫秒,我们将不可避免地错过下一帧,对吗?

【问题讨论】:

你想解决什么问题?你没有问具体的问题。根据您的 cmets,您似乎主要对计算假设的掉落率感兴趣。对于您在问题文本中所做的事情,哪一个永远是假设的。在实践中,您只需测量它。那么你到底想达到什么目的呢? 【参考方案1】:

    是的,在这种情况下,您将以远低于 60 fps 的速度触发 handleScroll - 取决于您的 handleScroll 回调正在做什么,您的用户可能会遇到一些卡顿。

    requestAnimationFrame会尽量保持60fps,但不保证60fps。根据可用的 CPU、GPU、内存和其他限制,它的运行速度可能会慢得多。

请注意,即使它确实以 >60fps 的速度运行,这也会为您(如您所指出的)提供 16-17 毫秒的帧预算,其中 执行您的回调操作。

因此,如果您的回调需要 100 毫秒 执行,那么你甚至无法获得流畅的 60fps 动画 使用requestAnimationFrame。铬的performance dev tools 可以帮助您确定导致动画延迟的原因,但这取决于 你优化你的回调在不到 17 毫秒内运行,以防止 丢帧。

Check out this article for a more in depth breakdown

【讨论】:

感谢您的回复。但我想获得一些定性分析或定量分析这种性能问题的方法。具体来说,我想了解(大致)这样的回调将丢弃多少帧(如果刷新率为 60hz)【参考方案2】:

您的假设中有两个注意事项,首先是您有 16 毫秒预算(60 赫兹)要花费,这是不正确的,因为浏览器已经进行了某种内部计算来绘制下一帧在 chrome 上需要相当长的时间大约 6 毫秒,因此我们有大约 10 毫秒作为explained here

第二个假设是设备将具有 60hz 刷新率,随着越来越多的设备使用高刷新率来提高滚动流畅度,甚至降低刷新率以节省电池,这将在不久的将来过时; 所以这些不是安全的假设

顺便说一下,原则是一样的,如果一个任务在主线程上花费了很长时间将停止所有其他任务直到它完成,让我们演示一下行动:

lag 函数模拟一个需要一段时间才能运行的 CPU 密集型任务; raf 函数将通过将递归 requestAnimationFrame 调度到自身来移动 200px,这将更改框的 translateX 属性;最后我们有一个laggyRaf,它使用lag函数来模拟一个长任务;

const box = document.querySelector('.box');
const x_move_distance = 200;

function lag (delay = 1000) 
  const time = Date.now();
  while ( Date.now() < time + delay ) 
    // waits 
  


function moveBox ( position ) 
  box.style.transform = `translateX($positionpx)`;


let counter = 0;
function raf() 
  moveBox(counter);
  if ( counter < x_move_distance ) 
    requestAnimationFrame(raf);
    counter++;
  


let counter2 = 0;
function laggyRaf() 
  moveBox(counter2);
  lag(100); //100 ms seconds extra lag
  if ( counter2 < x_move_distance ) 
    requestAnimationFrame(laggyRaf);
    counter2++;
  
.box 
  width: 100px;
  height: 100px;
  background: blue;
<div class='box'></div>
<button onclick="counter = 0; raf()">start raf animations</button>
<button onclick="lag()">start cpu-intensive task</button>
<br />
<button onclick="counter2 = 0; laggyRaf()">start laggy animations</button>

【讨论】:

谢谢您的回复。我知道显示刷新率将是 60hz 并不总是正确的,并且渲染过程中涉及更多内容。但感谢您的回复。但我想获得一些定性分析或定量分析方法来解决这样的性能问题。具体来说,我想(粗略地)了解这样的回调会丢弃多少帧(如果刷新率为 60hz) 感谢这次精彩的讨论,我也在这里了解 requestanimationframe/ html5rocks.com/en/tutorials/speed/rendering

以上是关于关于丢帧和 requestAnimationFrame 的困惑的主要内容,如果未能解决你的问题,请参考以下文章

dp83640丢帧

can接收丢帧 autosar

EasyRTMP获取H.264实时流并转化成为RTMP直播推流之EasyRTMP-iOS如何处理H264关键帧和SPSPPS数据的

OpenCV 实时流式视频捕获很慢。如何丢帧或实时同步?

对 iOS 上 SpriteKit 中的坐标、帧和子节点感到困惑?

Scala Spark 在数据帧和数据集中以不同方式处理 Double.NaN