0

我是一名实验心理学博士生,由于 COVID-19,我们必须在线切换所有实验。我也不太了解Javascript。

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

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

$presentation使用$fixationgenerateTrial(current_trial).

感谢您的帮助!

最好的问候,马丁康斯坦特。

4

1 回答 1

2

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

所以setTimeout平滑地动画视觉内容是不可靠的。

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

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

但是在这里,您无法流畅地为视觉内容制作动画。

我们所说的屏幕刷新率在绝大多数设备上都是 60Hz,即每帧 16.67 毫秒。
您的超时设置为 995 毫秒、195 毫秒和 495 毫秒。那里的最小间隔(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 );

于 2020-05-15T01:45:30.080 回答