我是一名实验心理学的博士生,由于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)
.
谢谢您的帮助
问候,马丁-康斯坦特。
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 );