我是实验心理学的博士生,由于 COVID-19,我们必须将所有实验切换到在线。我也不是很了解Javascript。
问题是我们通常会在短时间内(例如 200 毫秒(呈现刺激,并且我们需要最少的可变性,因此我们通常与显示器刷新率同步。
我对Javascript的有限理解是setTimeout()
与监视器帧无关(因此应该显示200ms的刺激实际上可能在屏幕上比此持续时间更长(,并且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()
更好的东西(? :)
我使用在generateTrial(current_trial)
期间绘制的HTML5画布($presentation
和$fixation
(。
感谢您的帮助!
此致敬意 马丁康斯坦特。
setTimeout
确实与帧刷新率不同步,它甚至会应用一些限制,如果他们决定其他任务更重要,浏览器可能会延迟(例如,如果它恰好发生在 setTimeout 应该解析的同时,他们可能更喜欢触发 UI 事件, 在递归循环中调用它总是会积累一些漂移时间。
因此,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 );