如何以指定的帧速绘制动画?用setInterval来调度误差大吗?下面的测试会给出解答。
环境和设定
浏览器
- Windows Chrome 108
- Android Chrome 107
- iOS Safari 16.0
- Windows Firefox 107
- Android Firefox 108 Beta
测试15fps的帧速(帧间隔毫秒为1000/15),测试持续60秒,期间页面一直保持显示状态(不切换到后台,不息屏)
测试代码
在线演示见CodePen setInterval actual interval test
Interval set: <span id="interval0">0</span><br/>
Average interval <span id="interval1">0</span><br/>
<script>
function main() {
let interval = 1000 / 15;
document.querySelector('#interval0').textContent = interval ;
let span = document.querySelector('#interval1');
let count = 0;
let startTime = performance.now();
let handle = setInterval(() => {
let date = new Date();
count++;
let avgInterval = (performance.now() - startTime) / count;
span.textContent = avgInterval.toFixed(2);
}, interval);
}
document.addEventListener('DOMContentLoaded', main);
</script>
测试结果
setInterval
的实测平均间隔
occasion \ platform | Windows Chrome | Android Chrome | macOS Safari | iOS Safari | Windows Firefox | Android Firefox |
---|---|---|---|---|---|---|
page visible | 66.01 | 66.01 | 66.69 | 78.45 | 66.67 | |
page visible (battery power saving mode) | 66.01 | 66.01 | 89.76 | 78.36 | 66.68 | |
page hidden | ||||||
page hidden (battery power saving mode) |
解读
根据以上测试结果,我能得出的结论是:
Chrome的setInterval似乎对delay参数进行了floor()
处理(不支持delay的小数部分),采取自校准策略使得调度较为准时(当参数delay为整数时调度不会顺延),似乎未针对省电模式切换策略。
Safari的setInterval分两种情况:非省电模式中支持delay参数的小数部分,采取自校准策略使得调度较为准时;省电模式中采取累积延迟策略(顺延)。
Windows Firefox的setInterval采取累积延迟策略,Android Firefox的setInterval采取自校准策略,两者似乎未针对省电模式切换策略。
这么乱,接下来怎么办
自校准策略和累积延迟策略的setInterval各有其适用场合,类似于对即时性要求高容许丢包的UDP协议和对准确要求高容许延迟的TCP协议适用与不同场景。
作者分别给出基于这两种策略的两套setInterval实现(见Gist setInterval implementations),使得前端开发者在不同的应用场景下选择不同策略的setInterval实现,当然开发者应优先检测浏览器的setInterval策略,如果合适则优先采用浏览器原生的setInterval。