JavaScript动画的持续时间并不准确

时间:2012-06-14 23:01:41

标签: javascript setinterval duration

首先,我想提两件事,
一个:我的代码并不完美(esspechially是eval部分) - 但我想为自己尝试一些东西,看看我是否可以复制jQuery Animation功能,所以请原谅我的“坏”的做法,请不要建议我使用jQuery,我想试验。
二:此代码尚未完成,我只是想弄清楚是什么让它无法正常工作。

所以动画运行大约12秒,而我输入的持续时间参数是15秒,我做错了什么?

function animate(elem, attr, duration){
  if(attr.constructor === Object){//check for object literal
    var i = 0;
    var cssProp = [];
    var cssValue = [];
    for(key in attr) {
          cssProp[i] =  key;
          cssValue[i] = attr[key];
    }
    var fps = (1000 / 60);
    var t = setInterval(function(){
      for(var j=0;j<cssProp.length;j++){
        if(document.getElementById(elem).style[cssProp[j]].length == 0){
          //asign basic value in css if the object dosn't have one.
         document.getElementById(elem).style[cssProp[j]]= 0;
        }
        var c = document.getElementById(elem).style[cssProp[j]];
        //console.log(str +" | "+c+"|"+cssValue[j]);
        if(c > cssValue[j]){
            document.getElementById(elem).style[cssProp[j]] -= 1/((duration/fps)*(c-cssValue[j]));
        }else if(c < cssValue[j]){
            document.getElementById(elem).style[cssProp[j]] += 1/((duration/fps)*(c-cssValue[j]));
        }else if(c == cssValue[j]){
            window.clearInterval(t);
        }
      }
    },fps);
  }
}
  animate('hello',{opacity:0},15000);

HTML:

  <p id="hello" style="opacity:1;">Hello World</p>

注意:我猜

存在问题
  

(持续时间/ FPS)*(C-cssValue [j]的)

部分或/和setInterval(fps变量)的间隔。


提前致谢

2 个答案:

答案 0 :(得分:4)

我不会尝试重构并解决它,因为它非常不稳定。那说......一些事情。

不要依赖您动画的价值来让您了解动画进度

一般来说,你的做法是不健全的。你最好自己跟踪进度。另外,由于你的方法,你的数学看起来似乎太努力了,应该更简单。

可以这样想:当时间结束时你的动画就完成了,而不是当动画值似乎表明它在最终位置时。

不要增加,设置

浮点数学是不精确的,像这样的重复加法累积也会累积浮点误差。并且可以使用一些变量来跟踪您的进度,这可以用来计算。

animatedValue += changeOnThisFrame // BAD!
animatedValue = valueOnThisFrame   // GOOD!

不要做正/负条件舞

事实证明10 + 1010 - (-10)实际上是一回事。这意味着您可以随时添加值,但更改率可以是负数或正数,并且该值将在适当的方向上设置动画。

超时和时间间隔不准确

结果setTimeout(fn, 50)实际上意味着至少在50ms后调度fn。在50ms之后执行的下一个JS运行循环将运行该函数,因此您不能完全依赖它。

那说它通常在几毫秒之内。但是帧的60fps大约是16ms,并且该定时器实际上可能在16-22ms的不同时间内发射。因此,当您根据帧速率进行计算时,它根本不会与实际接近的时间相匹配。

重构复杂数学

在这里解构这条线会很难。

document.getElementById(elem).style[cssProp[j]] -= 1/((duration/fps)*(c-cssValue[j]));

为什么更复杂的分解,以便您可以轻松了解这里发生了什么。单独重构这一行,我可能会这样做:

var style = document.getElementById(elem).style;
var changeThisFrame = duration/fps;
var someOddCalculatedValue = c-cssValue[j];
style[cssProp[j]] -= 1 / (changeThisFrame * someOddCalculatedValue);

这样做可以更清楚地表达数学中每个表达式的含义以及它的含义。而且因为你没有在这里做,所以我很难想知道为什么c-cssValue[j]在那里以及它代表什么。

简单示例

这比你的能力要差,但它显示了你应该采取的方法。它使用动画开始时间来创建完美的值,具体取决于动画的完整程度,开始的位置以及动画的位置。它不使用当前的动画值来确定任何内容,并保证运行动画的全长。

var anim = function(elem, duration) {

    // save when we started for calculating progress
    var startedAt = Date.now();

    // set animation bounds
    var startValue = 10;
    var endValue   = 200;

    // figure out how much change we have over the whole animation
    var delta = endValue - startValue;

    // Animation function, to run at 60 fps.
    var t = setInterval(function(){

        // How far are we into the animation, on a scale of 0 to 1.
        var progress = (Date.now() - startedAt) / duration;

        // If we passed 1, the animation is over so clean up.
        if (progress > 1) {
            alert('DONE! Elapsed: ' + (Date.now() - startedAt) + 'ms');
            clearInterval(t);
        }

        // Set the real value.
        elem.style.top = startValue + (progress * delta) + "px";

    }, 1000 / 60);
};

anim(document.getElementById('foo'), 5000);
​

JSFiddle:http://jsfiddle.net/DSRst/

答案 1 :(得分:3)

您无法使用setInterval来获得准确的总时间。因为JS是单线程的,并且多个事物在一个线程上竞争循环,所以不能保证下一个间隔调用将准确地准时或N个间隔将消耗确切的持续时间。

相反,几乎所有动画例程都获得当前时间并使用系统时钟来测量总持续时间的时间。一般算法是获取开始时间,计算所需的完成时间(开始时间+持续时间)。然后,正如您所做的那样,计算预期的步长值和迭代次数。然后,在每个步骤中,您重新计算剩余剩余时间和剩余步长值。通过这种方式,您可以确保动画始终准确(或几乎完全)按时完成,并始终准确到达最终位置。如果动画落后于理想轨迹,则它将自我校正并稍微移动其余步骤。如果由于任何原因(舍入错误等等)提前,它将拨回步长并同样按时到达最终位置。

您可能还需要知道浏览器并不总是支持非常小的时间量。每个浏览器都有一些允许定时器操作的最短时间。这是关于最小计时器级别的an article

这里有关于补间的an article(不断重新计算步骤以完全符合持续时间的过程)。

我还建议您查看在某些库(jQuery,YUI或您找到的任何其他库)中执行动画的代码,因为它们都可以以非常通用的方式向您展示如何完成此操作,包括补间,缓和功能等......