用 requestAnimationFrame() 替换 setTimeout() [英] Replacing setTimeout() with requestAnimationFrame()

查看:118
本文介绍了用 requestAnimationFrame() 替换 setTimeout()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是实验心理学的博士生,由于 COVID-19,我们必须将所有实验切换到网上.我也不太了解 Javascript.

I am a PhD student in experimental psychology and due to COVID-19, we have to switch all our experiments online. I also don't know Javascript very well.

问题在于,我们通常会在很短的时间内(例如 200 毫秒)呈现刺激,并且我们需要最少的可变性,因此我们通常会与显示器刷新率同步.

The problem is that we usually present stimuli for a short duration (e.g. 200ms) and we need the least amount of variability so we usually sync with the monitor refresh rate.

我对 Javascript 的有限理解是 setTimeout() 不依赖于监控帧(因此应该显示 200 毫秒的刺激实际上可能比这个持续时间更长),并且requestAnimationFrame() 会更精确.但是,过去几天我一直在尝试了解如何使用 requestAnimationFrame() 而不是 setTimeout() 但无济于事,我找到的所有教程都是针对显示动画刺激.这是我现在用于处理实验流程的代码片段.

My limited understanding of Javascript is that setTimeout() is not tied to monitor frames (so a stimulus that should be displayed for 200ms could actually be on the screen longer than this duration), and that requestAnimationFrame() would be more precise. However, I've spent the last few days trying to understand how to use requestAnimationFrame() instead of setTimeout() but to no avail, all the tutorials I found were for displaying animated stimuli. Here's the snippet of code I use right now for handling my experiment's flow.

setTimeout(function() {
    generateTrial(current_trial);
    $fixation.show();
    setTimeout(function() {
        $fixation.toggle();
        $presentation.toggle();
        setTimeout(function() {
            $presentation.toggle();
            $fixation.toggle();
            setTimeout(function() {
                ShowContinuousReport(current_trial);
            }, 995);
        }, 195);
    }, 995);
}, 495);

您是否知道如何将所有这些讨厌的 setTimeout() 转换为 requestAnimationFrame()(或者至少比 setTimeout())?:)

Would you have an idea on how to convert all these nasty setTimeout() to requestAnimationFrame() (or at least something better than setTimeout())? :)

我使用在 generateTrial(current_trial) 期间绘制的 HTML5 画布($presentation$fixation).

I use HTML5 canvases ($presentation and $fixation) that are drawn during generateTrial(current_trial).

感谢您的帮助!

最好的问候,马丁·康斯坦.

Best regards, Martin Constant.

推荐答案

setTimeout 确实与帧刷新率不同步,甚至会有一些节流,可能会延迟由浏览器决定,如果他们认为其他任务更重要(例如,他们可能更喜欢触发 UI 事件,如果它恰好在 setTimeout 应该解决的时间发生,并且在递归循环中调用它总是会累积一些 漂移时间.

setTimeout indeed is not in sync with the frame refresh rate, it will even have some throttling applied on it, and may be delayed by the browser if they decide an other task was more important (e.g, they may prefer to fire an UI event if it happens exactly at the same time the setTimeout was supposed to resolve, and calling it in a recursive loop will always accumulate some drift time.

所以 setTimeout 不能可靠地平滑动画视觉内容.

So setTimeout is not reliable to animate visual content smoothly.

另一方面,requestAnimationFrame 会安排回调在下一个绘制帧中触发,通常与屏幕刷新率同步.

On the other hand, requestAnimationFrame will schedule a callback to fire in the next painting frame, generally in sync with the screen refresh rate.

requestAnimationFrame 是平滑动画视觉内容的完美工具.

requestAnimationFrame is the perfect tool to animate visual content smoothly.

我们所说的屏幕刷新率在绝大多数设备上都是 60Hz,即每帧 16.67ms.
您的超时设置为 995 毫秒、195 毫秒和 495 毫秒.那里的最小间隔(195ms)大约对应于 12Hz 的频率,最大的几乎是 1Hz.

The screen refresh rate we are talking about is on the vast majority of devices 60Hz, that is 16.67ms per frame.
Your timeouts are set to 995ms 195ms, and 495ms. The smallest interval there (195ms) corresponds approximately to a 12Hz frequency, the biggest is almost 1Hz.

如果你真的需要它在长期内尽可能精确,那么在你的循环中加入漂移校正逻辑:

If you really need it to be as precise as possible for a long run, then incorporate a drift correction logic in your loop:

您记录开始时间,然后在每一步检查有多少漂移,并相应地调整下一次超时:

You record the starting time, then at each step, you check how much drift there was, and you adjust the next timeout accordingly:

这是一个基本示例,根据您的情况,但可能很难在如此小的样本上获得漂移校正的用处,但仍要注意漂移校正版本如何能够减少漂移,而在未修正的情况下,它总是会加起来.

Here is a basic example, based on your case, but it might be quite hard to get the usefulness of that drift correction on such a small sample, still pay attention on how the drift corrected version is able to reduce the drift, while in the non-corrected one, it will always add up.

const delays = [ 495, 995, 195, 995 ];

setTimeout(() => {
console.log( 'testing with drift correction' );
const start_time = performance.now();
let expected_time = start_time;
setTimeout( () => {
  // do your things here
  const now = performance.now();
  expected_time += delays[ 0 ];
  const drift = now - expected_time;
  setTimeout( () => {
    const now = performance.now();
    expected_time += delays[ 1 ];
    const drift = now - expected_time;
    setTimeout( () => {
      const now = performance.now();
      expected_time += delays[ 2 ];
      const drift = now - expected_time;
      setTimeout( () => {
        const now = performance.now();
        expected_time += delays[ 3 ];
        const drift = now - expected_time;
        console.log( 'last step drift corrected:', drift );
      }, delays[ 3 ] - drift );
      console.log( 'third step drift corrected:', drift );
    }, delays[ 2 ] - drift );
    console.log( 'second step drift corrected:', drift );
  }, delays[ 1 ] - drift );
  console.log( 'first step drift corrected:', drift );
}, delays[ 0 ] );

}, 100 );

setTimeout( () => {

console.log( 'testing without drift correction' );
const start_time = performance.now();
let expected_time = start_time;

setTimeout( () => {
  // do your things here
  const now = performance.now();
  expected_time += delays[ 0 ];
  const drift = now - expected_time;
  setTimeout( () => {
    const now = performance.now();
    expected_time += delays[ 1 ];
    const drift = now - expected_time;
    setTimeout( () => {
      const now = performance.now();
      expected_time += delays[ 2 ];
      const drift = now - expected_time;
      setTimeout( () => {
        const now = performance.now();
        expected_time += delays[ 3 ];
        const drift = now - expected_time;
        console.log( 'last step drift not corrected:', drift );
      }, delays[ 3 ] );
      console.log( 'last step drift not corrected:', drift );
    }, delays[ 2 ] );
    console.log( 'last step drift not corrected:', drift );
  }, delays[ 1 ] );
  console.log( 'last step drift not corrected:', drift );
}, delays[ 0 ] );
}, 3000 );

这篇关于用 requestAnimationFrame() 替换 setTimeout()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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