这一段使用setInterval制作的动画,出现时间误差的原因是什么?

详细代码: http://jsfiddle.net/7FX3X/17/
关注者
18
被浏览
8,430

5 个回答

查了些资料,有了一个比较合理的解释。

setInterval计时并不准确
假设有如下代码:
setInterval(function () {func(i++) }, 100)
每隔100ms执行i累加一次。
如下图所示,红色方块是函数执行时间;方块之间的时间,也就是执行间隔时间是少于100ms的。

当函数执行时间超过间隔时间时,后续的函数执行会加入一个队列,由此造成总执行时间超出预期。


具体到问题中,每15ms更新一次动画,函数执行时间超出15ms时,会延迟后续函数的执行,造成执行总时间超出1000ms。
如果将执行时间gapTime加大,比如到250ms,那么执行时间相差无几:jsfiddle.net/7FX3X/24/

解决方法
  1. 根据时间差来计算当前需要移动到的距离,而不是根据速度。
  2. 递归调用setTimeout,而不是setInterval。setInterval在调用一些执行时间超出时间间隔的函数时会将后续函数加入队列,造成总时间不稳定。递归调用setTimeout可以保证某次函数、动画执行完毕后,再进行下一帧动画。
修改版:jsfiddle.net/7FX3X/21/

参考资料
  1. javascript.info/tutoria
  2. stackoverflow.com/quest
  3. stackoverflow.com/quest
继续浏览内容
知乎
发现更大的世界
打开
浏览器
继续
jQuery的的animate方法是用当前的时间来决定位移的,这样可以最大限度的保证运行时长的准确性。

我模拟了一段,很不完善,但思路接近吧,你试试,跑的比你快,但是比他慢。(:D)

建议将time改为10秒试试
var start = new Date;
var end = $(document).width() - $('#box1').width();
$('#box1').animate({
left: end
}, 10000);

// TODO: move #box2 like #box1 (using NATIVE javascript DOM api)
// ...
// to do
// 缺陷:有时间误差
!function () {
var box2 = document.querySelector('#box2');
var totalWidth = end ;
var time = 10000; //总时间
var gapTime = 15; //动画间隔

//每次位移距离
var speed = +(totalWidth / time * gapTime).toFixed(0);

var left = 0;
var move = function () {
left += speed;
box2.style.left = left + 'px';
if (left >= totalWidth) {
box2.style.left = totalWidth + 'px';
clearInterval(action);
return;
}
}

var action = setInterval(move, gapTime);
}();

(function(){
// Box3 JS 计算动画时长,这里采用 13ms,使用 setInterval 循环,直到动画时长超过 1000ms
var box3 = document.querySelector("#box3");
var totalWidth = end;
var time = 10000;
var gapTime = 13;
var starttime;

var timenow = function() {
return ( new Date() ).getTime();
}

var move = function(){
if(!starttime){
starttime = timenow();
}
var tnow = timenow();
var p = (tnow - starttime) / time;
var pos = 0.5 - Math.cos( p*Math.PI ) / 2;
var w = totalWidth * pos;


if( w > totalWidth){
w = totalWidth;
}

box3.style.left = w + 'px';

if(p > 1){
clearInterval(action);
return;
}
}

var action = setInterval(move, gapTime);
})()
继续浏览内容
知乎
发现更大的世界
打开
浏览器
继续