用 requestAnimationFrame() 替换 setTimeout()
Replacing setTimeout() with requestAnimationFrame()
我是一名实验心理学博士生,由于 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()
更好的东西)? :)
我使用在 generateTrial(current_trial)
期间绘制的 HTML5 画布($presentation
和 $fixation
)。
感谢您的帮助!
此致,
马丁·康斯坦。
setTimeout
确实与帧刷新率不同步,它甚至会应用一些节流,如果浏览器认为其他任务更重要(例如,他们可能更愿意触发一个 UI 事件,如果它恰好发生在 setTimeout 应该解决的同一时间,并且在递归循环中调用它总是会累积一些 drift 时间.
所以 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 );
我是一名实验心理学博士生,由于 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()
更好的东西)? :)
我使用在 generateTrial(current_trial)
期间绘制的 HTML5 画布($presentation
和 $fixation
)。
感谢您的帮助!
此致, 马丁·康斯坦。
setTimeout
确实与帧刷新率不同步,它甚至会应用一些节流,如果浏览器认为其他任务更重要(例如,他们可能更愿意触发一个 UI 事件,如果它恰好发生在 setTimeout 应该解决的同一时间,并且在递归循环中调用它总是会累积一些 drift 时间.
所以 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 );