用 requestAnimationFrame() 替换 setTimeout() 。

问题描述 投票:0回答:1

我是一名实验心理学的博士生,由于COVID-19的原因,我们所有的实验都要转到网上进行。我对Javascript也不是很了解。

问题是我们通常呈现的刺激持续时间很短(例如200ms),我们需要最小的变化量,所以我们通常与显示器刷新率同步。

我对Javascript的有限理解是 setTimeout() 不与显示器帧数挂钩(所以一个应该显示200毫秒的刺激,实际上在屏幕上的时间可能会比这个时间长),而且 requestAnimationFrame() 会更精确。然而,我在过去的几天里一直在尝试了解如何使用 requestAnimationFrame() 而不是 setTimeout() 但无济于事,我找到的所有教程都是用来显示动画刺激的。下面是我现在用来处理我的实验流程的代码片段。

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())? :)

我使用HTML5画布($presentation$fixation),是在 generateTrial(current_trial).

谢谢您的帮助

问候,马丁-康斯坦特。

javascript settimeout timing requestanimationframe experimental-design
1个回答
0
投票

setTimeout 确实是与帧刷新率不同步的,它甚至会有一些节流应用在上面,如果浏览器决定有其他任务更重要的话,可能会被浏览器延迟(例如,如果一个UI事件正好发生在setTimeout应该解析的同一时间,他们可能更喜欢启动这个事件,而在递归循环中调用它总是会积累一些 漂移 时候。

所以 setTimeout 是不可靠的,无法流畅地制作视觉内容的动画。

另一方面,。requestAnimationFrame 将安排回调在下一个绘画帧中启动,一般与屏幕刷新率同步。

requestAnimationFrame 是流畅动画视觉内容的完美工具。

但这里你并不是在流畅地制作视觉内容的动画。

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

你要做的是调度任务,为此。setTimeout 是最好的。

如果你真的需要它尽可能精确地长时间运行,那么在你的循环中加入漂移修正逻辑。

你记录下起始时间,然后在每一步,你检查有多少漂移, 然后你相应地调整下一个超时。

这是一个基本的例子,基于你的情况, 但它可能是相当困难的,以获得有用的漂移校正 在这样一个小样本,仍然注意如何漂移校正版本 能够减少漂移,而在非校正的,它总是会增加。

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 );
© www.soinside.com 2019 - 2024. All rights reserved.