显示的确切时间:requestAnimationFrame 用法和时间轴 [英] Exact time of display: requestAnimationFrame usage and timeline

查看:40
本文介绍了显示的确切时间:requestAnimationFrame 用法和时间轴的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要实现的是检测屏幕上出现某种变化的精确时间(主要是谷歌浏览器).例如,我使用 $("xelement").show(); 显示一个项目或使用 $("#xelement").text("sth new"); 更改它code> 然后我想看看在给定屏幕重绘的用户屏幕上出现更改时 performance.now() 究竟是什么.所以我对任何解决方案都持完全开放的态度——下面我主要指的是 requestAnimationFrame (rAF),因为这是应该帮助实现这一目标的功能,只是它似乎没有;见下文.

基本上,正如我想象的那样,rAF 应该在大约 0-17 毫秒内执行其中的所有内容(每当下一帧出现在我的标准 60 Hz 屏幕上时).此外,timestamp 参数应该给出这次执行的时间值(并且这个值基于与 performance.now() 相同的 DOMHighResTimeStamp 度量).

现在这是我为此进行的众多测试之一:https://jsfiddle.net/gasparl/k5nx7zvh/31/

function item_display() {var before = performance.now();requestAnimationFrame(函数(时间){var r_start = performance.now();var r_ts = 时间;console.log("before:", before);console.log("RAF 回调开始:", r_start);console.log("英国皇家空军印章:", r_ts);console.log("before vs. RAF callback start:", r_start - before);console.log("before vs. RAF stamp:", r_ts - before);控制台日志(")});}setInterval(item_display, Math.floor(Math.random() * (1000 - 500 + 1)) + 500);

我在 Chrome 中看到的是:rAF 中的函数总是在大约 0-3 毫秒内执行(从它之前的 performance.now() 开始计算),而且,最奇怪的是,rAF 时间戳与我在 rAF 中使用 performance.now() 得到的结果,通常比在 rAF 之前调用的 performance.now() 早大约 0-17 毫秒(但有时大约 0-1 毫秒之后).

这是一个典型的例子:

之前:409265.00000001397RAF 回调开始:409266.30000001758英国皇家空军邮票:409260.832在 vs. RAF 回调开始之前:1.30000000353902之前对阵英国皇家空军邮票:-4.168000013974961

在 Firefox 和 IE 中是不同的.在 Firefox 中,before vs. RAF 回调开始"是大约 1-3 毫秒或大约 16-17 毫秒.before vs. RAF 标记"总是积极的,通常在 0-3 毫秒左右,但有时在 3-17 毫秒之间.在 IE 中,两者的差异几乎总是在 15-18 毫秒左右(正).这些或多或少与不同的 PC 相同.然而,当我在手机的 Chrome 上运行它时,然后,并且只有那时,它似乎似乎是正确的:在与 RAF 标记之前"随机在 0-17 左右,而RAF 回调开始"总是在几毫秒之后.

更多上下文:这是一个在线响应时间实验,用户使用他们自己的 PC(但我通常将浏览器限制为 Chrome,所以这是唯一对我真正重要的浏览器).我重复显示各种项目,并将响应时间测量为从元素显示的那一刻(当人们看到它时)到他们按下一个键的那一刻",并从记录的响应时间中计算出特定的平均值项目,然后检查某些项目类型之间的差异.这也意味着如果记录的时间总是在一个方向上有点偏斜(例如总是在元素实际出现之前 3 ms),只要这种偏斜对于每个显示都是一致的,这并不重要,因为只有差异真的很重要.1-2 毫秒的精度将是理想的,但任何能减轻随机刷新率噪声"(0-17 毫秒)的东西都会很好.

我也尝试了 jQuery.show() 回调,但它没有考虑刷新率:https://jsfiddle.net/gasparl/k5nx7zvh/67/

var r_start;功能显示(){r_start = performance.now();}函数 item_display() {var before = performance.now();$("#stim_id").show(complete = shown())var after = performance.now();var text = "before: " + before + "<br>callback RT: " + r_start + "
after:" + after + "
before vs. callback:" + (r_start - before)+ "<br>之前与之后:" + (after - r_start)控制台日志(")控制台日志(文本)$("p").html(text);setTimeout(function(){ $("#stim_id").hide(); }, 500);}setInterval(item_display, Math.floor(Math.random() * (1000 - 500 + 1)) + 800);

使用 HTML:






<span id="stim_id">刺激</span>

<小时>

解决方案(基于 Kaiido 的回答)以及工作显示示例:

function monkeyPatchRequestPostAnimationFrame() {const channel = new MessageChannel();const 回调 = [];让时间戳 = 0;让被称为 = 假;channel.port2.onmessage = e =>{叫 = 假;const toCall = callbacks.slice();callbacks.length = 0;toCall.forEach(fn => {尝试 {fn(时间戳);} 赶上 (e) {}});};window.requestPostAnimationFrame = 函数(回调){如果(回调类型!=='函数'){throw new TypeError('参数 1 不可调用');}callbacks.push(callback);如果(!调用){requestAnimationFrame((时间) => {时间戳 = 时间;channel.port1.postMessage('');});被称为 = 真;}};}if (typeof requestPostAnimationFrame !== 'function') {monkeyPatchRequestPostAnimationFrame();}功能 chromeWorkaroundLoop() {如果需要的话) {requestAnimationFrame(chromeWorkaroundLoop);}}//这是我显示项目的方式//包括 100 毫秒的预热"函数 item_display() {window.needed = true;chromeWorkaroundLoop();设置超时(函数(){var before = performance.now();$("#stim_id").text("随机新文本:" + Math.round(Math.random()*1000) + ".");$("#stim_id").show();//我要求在上面显示,并在下面获取显示时间requestPostAnimationFrame(function() {var rPAF_now = performance.now();console.log("之前 vs. rPAF 现在:", rPAF_now - 之前);console.log("");需要=假;});}, 100);}//下面只是运行显示内容的示例实例函数example_loop(计数){$("#stim_id").hide();设置超时(函数(){item_display();如果(计数> 1){example_loop(--count);}}, Math.floor(Math.random() * (1000 - 500 + 1)) + 500);}example_loop(10);

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js"></script><div id="stim_id">任何文本</div>

解决方案

您遇到了什么 是一个 Chrome 错误(甚至两个).

基本上,当 requestAnimationFrame 回调池为空时,它们会在当前事件循环结束时直接调用它,而无需等待规范要求的实际绘制帧.>

要解决此错误,您可以保持持续的 requestAnimationFrame 循环,但请注意,这会将您的文档标记为动画".并且会在你的页面上触发一堆副作用(比如在每次屏幕刷新时强制重绘).所以我不确定你在做什么,但这样做通常不是一个好主意,我宁愿邀请你只在需要时运行这个动画循环.

let needed = true;//当您不再需要 rAF 循环时设置为 false函数 item_display() {var before = performance.now();请求动画帧(函数(时间){var r_start = performance.now();var r_ts = 时间;console.log("before:", before);console.log("RAF 回调开始:", r_start);console.log("英国皇家空军印章:", r_ts);console.log("before vs. RAF callback start:", r_start - before);console.log("before vs. RAF stamp:", r_ts - before);控制台日志(")setTimeout(item_display, Math.floor(Math.random() * (1000 - 500 + 1)) + 500);});}chromeWorkaroundLoop();item_display();功能 chromeWorkaroundLoop() {如果需要的话) {requestAnimationFrame(chromeWorkaroundLoop);}};

现在,requestAnimationFrame 回调在下一次绘制之前触发(实际上在同一个事件循环中),并且 TimeStamp 参数应该表示所有主要任务和微任务之后的时间当前帧被执行,在它开始它的更新渲染"之前子任务(第 9 步此处).
:然而,这并不是浏览器真正实现的,参见 此问答了解更多详情.

所以这不是你能拥有的最精确的,你是对的,在这个回调中使用 performance.now() 应该让你更接近实际的绘画时间.

此外,当 Chrome 在这里遇到另一个错误时,可能与第一个错误有关,当他们确实将此 rAF 时间戳设置为...我必须承认我不知道什么...也许是前一个绘画帧的时间戳.

(function() {让 raf_id,eventLoopReport = {编号:0,时间戳:0,现在:0},报告 = {nb_of_loops_between_call_and_start: -1,mouseClick_timeStamp: 0,呼叫任务:{事件循环:空,现在:0},rAF_task:{事件循环:空,现在:0,时间戳:0}};startEventLoopCounter();btn.onclick = triggerSingleFrame;//在每个事件循环中增加 eventLoop_id//(或者至少每次我们的 postMessage 循环触发时)函数 startEventLoopCounter() {const channel = new MessageChannel()channel.port2.onmessage = e =>{eventLoopReport.id ++;eventLoopReport.timeStamp = e.timeStamp;eventLoopReport.now = performance.now();channel.port1.postMessage('*');};channel.port1.postMessage('*');}函数triggerSingleFrame(e) {//mouseClick 事件至少应该在前一个事件循环中产生,所以它的timeStamp应该是过去的report.mouseClick_timeStamp = e.timeStamp;const report_calling = report.calling_task;report_calling.now = performance.now();report_calling.eventLoop = Object.assign({}, eventLoopReport);取消动画帧(raf_id);raf_id = requestAnimationFrame((raf_ts) => {const report_rAF = report.rAF_task;report_rAF.now = performance.now();report_rAF.timeStamp = raf_ts;report_rAF.eventLoop = Object.assign({}, eventLoopReport);report.nb_of_loops_between_call_and_start = report_rAF.eventLoop.id - report_calling.eventLoop.id;//这应该总是积极的report_el.textContent = "rAF.timeStamp - mouse_click.timeStamp:" +(report.rAF_task.timeStamp - report.mouseClick_timeStamp) + '\n\n' +//详细JSON.stringify(report, null, 2) ;});}})();

<button id="btn">flash</button><div id="out"></div><pre id="report_el"></pre>

再一次,运行无限 rAF 循环将修复这个奇怪的错误.

因此,您可能想要检查的一件事是可能传入 requestPostAnimationFrame 方法.

您可以在 Chrome 中访问它,1 启用实验性网络平台功能"后;在 chrome:flags 中.如果 html 标准接受此方法,我们将允许我们在绘制操作发生后立即触发回调.

从那里开始,您应该离这幅画最近.

var needed = true;函数 item_display() {var before = performance.now();请求动画帧(函数(){requestPostAnimationFrame(function() {var rPAF_now = performance.now();console.log("之前 vs. rPAF 现在:", rPAF_now - 之前);console.log("");setTimeout(item_display, Math.floor(Math.random() * (1000 - 500 + 1)) + 500);});});}if (typeof requestPostAnimationFrame === '函数') {chromeWorkaroundLoop();item_display();} 别的 {console.error("您的浏览器不支持 'requestPostAnimationFrame' 方法,请确保您在 chrome:flags 中启用了 'Experimental Web Platform features'");}功能 chromeWorkaroundLoop() {如果需要的话) {requestAnimationFrame(chromeWorkaroundLoop);}};

对于尚未实现该提案的浏览器,或者如果该提案从未通过规范实现,您可以尝试使用 MessageEvent 对其进行 polyfill,这应该是在下一个事件循环中首先触发的事件.

//polyfills requestPostAnimationFrame//requestPostAnimationFrame 填充if (typeof requestPostAnimationFrame !== "function") {(() => {const channel = new MessageChannel();const 回调 = [];让时间戳 = 0;让被称为 = 假;让预定=假;//使其从 rAF 工作让 inRAF = 假;//使其从 rAF 工作channel.port2.onmessage = e =>{被称为 = 假;const toCall = callbacks.slice();callbacks.length = 0;toCall.forEach(fn => {尝试 {fn(时间戳);} 赶上 (e) {}});}//我们需要覆盖 rAF 以让我们知道我们在 rAF 回调中//为了避免调度另一个 rAF,这将延迟一个绘制帧//我们可以在 rAF 上挂一个无限循环,但这意味着//强制文档一直动画//这对性能不利const rAF = globalThis.requestAnimationFrame;globalThis.requestAnimationFrame = function(...args) {如果(!预定){预定 = 真;rAF.call(globalThis, (time) => inRAF = time);globalThis.requestPostAnimationFrame(() => {预定=假;inRAF = 假;});}rAF.apply(globalThis, args);};globalThis.requestPostAnimationFrame = 函数(回调){if (typeof callback !== "function") {throw new TypeError("参数 1 不可调用");}callbacks.push(callback);如果(!调用){如果(英国皇家空军){时间戳 = inRAF;channel.port1.postMessage("");} 别的 {requestAnimationFrame((时间) => {时间戳 = 时间;channel.port1.postMessage("");});}被称为 = 真;}};})();}var需要=真;函数 item_display() {var before = performance.now();requestPostAnimationFrame(function() {var rPAF_now = performance.now();console.log("之前 vs. rPAF 现在:", rPAF_now - 之前);console.log("");setTimeout(item_display, Math.floor(Math.random() * (1000 - 500 + 1)) + 500);});}chromeWorkaroundLoop();item_display();功能 chromeWorkaroundLoop() {如果需要的话) {requestAnimationFrame(chromeWorkaroundLoop);}};

  1. 事实证明,此功能显然已从 Chrome 实验中删除.查看实施问题 我不知道为什么,什么时候,也不知道他们是否打算继续工作.

What I want to achieve is to detect the precise time of when a certain change appeared on the screen (primarily with Google Chrome). For example I show an item using $("xelement").show(); or change it using $("#xelement").text("sth new"); and then I would want to see what the performance.now() was exactly when the change appeared on the user's screen with the given screen repaint. So I'm totally open to any solutions - below I just refer primarily to requestAnimationFrame (rAF) because that is the function that is supposed to help achieve exactly this, only it doesn't seem to; see below.

Basically, as I imagine, rAF should execute everything inside it in about 0-17 ms (whenever the next frame appears on my standard 60 Hz screen). Moreover, the timestamp argument should give the value of the time of this execution (and this value is based on the same DOMHighResTimeStamp measure as performance.now()).

Now here is one of the many tests I made for this: https://jsfiddle.net/gasparl/k5nx7zvh/31/

function item_display() {
    var before = performance.now();
    requestAnimationFrame(function(timest){
        var r_start = performance.now();
        var r_ts = timest;
        console.log("before:", before);
        console.log("RAF callback start:", r_start);
        console.log("RAF stamp:", r_ts);
        console.log("before vs. RAF callback start:", r_start - before);
        console.log("before vs. RAF stamp:", r_ts - before);
        console.log("")
    });
}
setInterval(item_display, Math.floor(Math.random() * (1000 - 500 + 1)) + 500);

What I see in Chrome is: the function inside rAF is executed always within about 0-3 ms (counting from a performance.now() immediately before it), and, what's weirdest, the rAF timestamp is something totally different from what I get with the performance.now() inside the rAF, being usually about 0-17 ms earlier than the performance.now() called before the rAF (but sometimes about 0-1 ms afterwards).

Here is a typical example:

before: 409265.00000001397
RAF callback start: 409266.30000001758
RAF stamp: 409260.832 
before vs. RAF callback start: 1.30000000353902
before vs. RAF stamp: -4.168000013974961 

In Firefox and in IE it is different. In Firefox the "before vs. RAF callback start" is either around 1-3 ms or around 16-17 ms. The "before vs. RAF stamp" is always positive, usually around 0-3 ms, but sometimes anything between 3-17 ms. In IE both differences are almost always around 15-18 ms (positive). These are more or less the same of different PCs. However, when I run it on my phone's Chrome, then, and only then, it seems plausibly correct: "before vs. RAF stamp" randomly around 0-17, and "RAF callback start" always a few ms afterwards.

For more context: This is for an online response-time experiment where users use their own PC (but I typically restrict browser to Chrome, so that's the only browser that really matters to me). I show various items repeatedly, and measure the response time as "from the moment of the display of the element (when the person sees it) to the moment when they press a key", and count an average from the recorded response times for specific items, and then check the difference between certain item types. This also means that it doesn't matter much if the recorded time is always a bit skewed in a direction (e.g. always 3 ms before the actual appearance of the element) as long as this skew is consistent for each display, because only the difference really matters. A 1-2 ms precision would be the ideal, but anything that mitigates the random "refresh rate noise" (0-17 ms) would be nice.

I also gave a try to jQuery.show() callback, but it does not take refresh rate into account: https://jsfiddle.net/gasparl/k5nx7zvh/67/

var r_start;
function shown() {
    r_start = performance.now();
}
function item_display() {
    var before = performance.now();
    $("#stim_id").show(complete = shown())
    var after = performance.now();
    var text = "before: " + before + "<br>callback RT: " + r_start + "<br>after: " + after + "<br>before vs. callback: " + (r_start - before) + "<br>before vs. after: " + (after - r_start)
    console.log("")
    console.log(text)
    $("p").html(text);
    setTimeout(function(){ $("#stim_id").hide(); }, 500);
}
setInterval(item_display, Math.floor(Math.random() * (1000 - 500 + 1)) + 800);

With HTML:

<p><br><br><br><br><br></p>
<span id="stim_id">STIMULUS</span>


The solution (based on Kaiido's answer) along with working display example:

function monkeyPatchRequestPostAnimationFrame() {
  const channel = new MessageChannel();
  const callbacks = [];
  let timestamp = 0;
  let called = false;
  channel.port2.onmessage = e => {
    called = false;
    const toCall = callbacks.slice();
    callbacks.length = 0;
    toCall.forEach(fn => {
      try {
        fn(timestamp);
      } catch (e) {}
    });
  };
  window.requestPostAnimationFrame = function(callback) {
    if (typeof callback !== 'function') {
      throw new TypeError('Argument 1 is not callable');
    }
    callbacks.push(callback);
    if (!called) {
      requestAnimationFrame((time) => {
        timestamp = time;
        channel.port1.postMessage('');
      });
      called = true;
    }
  };
}

if (typeof requestPostAnimationFrame !== 'function') {
  monkeyPatchRequestPostAnimationFrame();
}

function chromeWorkaroundLoop() {
  if (needed) {
    requestAnimationFrame(chromeWorkaroundLoop);
  }
}

// here is how I display items
// includes a 100 ms "warm-up"
function item_display() {
  window.needed = true;
  chromeWorkaroundLoop();
  setTimeout(function() {
    var before = performance.now();
    $("#stim_id").text("Random new text: " + Math.round(Math.random()*1000) + ".");
    $("#stim_id").show();
    // I ask for display above, and get display time below
    requestPostAnimationFrame(function() {
      var rPAF_now = performance.now();
      console.log("before vs. rPAF now:", rPAF_now - before);
      console.log("");
      needed = false;
    });
  }, 100);
}

// below is just running example instances of displaying stuff
function example_loop(count) {
  $("#stim_id").hide();
  setTimeout(function() {
    item_display();
    if (count > 1) {
      example_loop(--count);
    }
  }, Math.floor(Math.random() * (1000 - 500 + 1)) + 500);
}

example_loop(10);

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js"></script>
<div id="stim_id">Any text</div>

解决方案

What you are experiencing is a Chrome bug (and even two).

Basically, when the pool of requestAnimationFrame callbacks is empty, they'll call it directly at the end of the current event loop, without waiting for the actual painting frame as the specs require.

To workaround this bug, you can keep an ever-going requestAnimationFrame loop, but beware this will mark your document as "animated" and will trigger a bunch of side-effects on your page (like forcing a repaint at every screen refresh). So I'm not sure what you are doing, but it's generally not a great idea to do this, and I would rather invite you to run this animation loop only when required.

let needed = true; // set to false when you don't need the rAF loop anymore

function item_display() {
  var before = performance.now();
  requestAnimationFrame(function(timest) {
    var r_start = performance.now();
    var r_ts = timest;
    console.log("before:", before);
    console.log("RAF callback start:", r_start);
    console.log("RAF stamp:", r_ts);
    console.log("before vs. RAF callback start:", r_start - before);
    console.log("before vs. RAF stamp:", r_ts - before);
    console.log("")
    setTimeout(item_display, Math.floor(Math.random() * (1000 - 500 + 1)) + 500);
  });
}
chromeWorkaroundLoop();
item_display();

function chromeWorkaroundLoop() {
  if (needed) {
    requestAnimationFrame(chromeWorkaroundLoop);
  }
};

Now, requestAnimationFrame callbacks fire before the next paint (actually in the same event loop), and the TimeStamp argument should represent the time after all main tasks and microtasks of the current frame were executed, before it's starts its "update the rendering" sub-task (step 9 here).
[edit]: However it's not really what browsers implement, see this Q/A for more details.

So it's not the most precise you can have, and you are right that using performance.now() inside this callback should get you closer to the actual painting time.

Moreover when Chrome faces yet an other bug here, probably related to the first one, when they do set this rAF timeStamp to... I must admit I don't know what... maybe the previous painting frame's timeStamp.

(function() {
let raf_id,
  eventLoopReport = {
    id: 0,
    timeStamp: 0,
    now: 0
  },
  report = {
    nb_of_loops_between_call_and_start: -1,
    mouseClick_timeStamp: 0,
    calling_task: {
        eventLoop: null,
      now: 0
    },
    rAF_task: {
        eventLoop: null,
      now: 0,
      timeStamp: 0
    }
  };
  
startEventLoopCounter();
  
btn.onclick = triggerSingleFrame;


// increments eventLoop_id at every event loop
// (or at least every time our postMessage loop fires)
function startEventLoopCounter() {
  const channel = new MessageChannel()
  channel.port2.onmessage = e => {
    eventLoopReport.id ++;
    eventLoopReport.timeStamp = e.timeStamp;
    eventLoopReport.now = performance.now();
    channel.port1.postMessage('*');
  };
  channel.port1.postMessage('*');
}

function triggerSingleFrame(e) {
  // mouseClick Event should be generated at least the previous event loop, so its timeStamp should be in the past
    report.mouseClick_timeStamp = e.timeStamp;
    const report_calling = report.calling_task;
  report_calling.now = performance.now();
  report_calling.eventLoop = Object.assign({}, eventLoopReport);

    cancelAnimationFrame(raf_id);
  
    raf_id = requestAnimationFrame((raf_ts) => {
    const report_rAF = report.rAF_task;
        report_rAF.now = performance.now();
    report_rAF.timeStamp = raf_ts;
    report_rAF.eventLoop = Object.assign({}, eventLoopReport);
    report.nb_of_loops_between_call_and_start = report_rAF.eventLoop.id - report_calling.eventLoop.id;
    // this should always be positive
    report_el.textContent = "rAF.timeStamp - mouse_click.timeStamp: " +
            (report.rAF_task.timeStamp - report.mouseClick_timeStamp) + '\n\n' +
      // verbose
        JSON.stringify(report, null, 2) ;
  });
}
})();

<button id="btn">flash</button>
<div id="out"></div>
<pre id="report_el"></pre>

Once again, running an infinite rAF loop will fix this weird bug.

So one thing you might want to check is the maybe incoming requestPostAnimationFrame method.

You can access it in Chrome,1 after you enable "Experimental Web Platform features" in chrome:flags. This method if accepted by html standards will allow us to fire callbacks immediately after the paint operation occurred.

From there, you should be at the closest of the painting.

var needed = true;
function item_display() {
  var before = performance.now();
  requestAnimationFrame(function() {
    requestPostAnimationFrame(function() {
      var rPAF_now = performance.now();
      console.log("before vs. rPAF now:", rPAF_now - before);
      console.log("");
      setTimeout(item_display, Math.floor(Math.random() * (1000 - 500 + 1)) + 500);
    });
  });
}

if (typeof requestPostAnimationFrame === 'function') {
  chromeWorkaroundLoop();
  item_display();
} else {
  console.error("Your browser doesn't support 'requestPostAnimationFrame' method, be sure you enabled 'Experimental Web Platform features' in chrome:flags");
}

function chromeWorkaroundLoop() {
  if (needed) {
    requestAnimationFrame(chromeWorkaroundLoop);
  }
};

And for the browsers that do not yet implement this proposal, or if this proposal never does it through the specs, you can try to polyfill it using a MessageEvent, which should be the first thing to fire at the next event loop.

// polyfills requestPostAnimationFrame
// requestPostAnimationFrame polyfill
if (typeof requestPostAnimationFrame !== "function") {
  (() => {
    const channel = new MessageChannel();
    const callbacks = [];
    let timestamp = 0;
    let called = false;
    let scheduled = false; // to make it work from rAF
    let inRAF = false; // to make it work from rAF
    channel.port2.onmessage = e => {
      called = false;
      const toCall = callbacks.slice();
      callbacks.length = 0;
      toCall.forEach(fn => {
        try {
          fn(timestamp);
        } catch (e) {}
      });
    }
    // We need to overwrite rAF to let us know we are inside an rAF callback
    // as to avoid scheduling yet an other rAF, which would be one painting frame late
    // We could have hooked an infinite loop on rAF, but this means
    // forcing the document to be animated all the time
    // which is bad for perfs
    const rAF = globalThis.requestAnimationFrame;
    globalThis.requestAnimationFrame = function(...args) {
      if (!scheduled) {
        scheduled = true;
        rAF.call(globalThis, (time) => inRAF = time);
        globalThis.requestPostAnimationFrame(() => {
          scheduled = false;
          inRAF = false;
        });
      }
      rAF.apply(globalThis, args);
    };
    globalThis.requestPostAnimationFrame = function(callback) {
      if (typeof callback !== "function") {
        throw new TypeError("Argument 1 is not callable");
      }
      callbacks.push(callback);
      if (!called) {
        if (inRAF) {
          timestamp = inRAF;
          channel.port1.postMessage("");
        } else {
          requestAnimationFrame((time) => {
            timestamp = time;
            channel.port1.postMessage("");
          });
        }
        called = true;
      }
    };
  })();
}

var needed = true;

function item_display() {
  var before = performance.now();
  requestPostAnimationFrame(function() {
    var rPAF_now = performance.now();
    console.log("before vs. rPAF now:", rPAF_now - before);
    console.log("");
    setTimeout(item_display, Math.floor(Math.random() * (1000 - 500 + 1)) + 500);
  });
}


chromeWorkaroundLoop();
item_display();

function chromeWorkaroundLoop() {
  if (needed) {
    requestAnimationFrame(chromeWorkaroundLoop);
  }
};

  1. Turns out this feature has apparently been removed from Chrome experiments. Looking at the implementation issue I can't find why, when, nor if they plan to still work on it.

这篇关于显示的确切时间:requestAnimationFrame 用法和时间轴的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆