动画的JavaScript持续时间不是';不确切

动画的JavaScript持续时间不是';不确切,javascript,setinterval,duration,Javascript,Setinterval,Duration,首先我想提两件事, One:我的代码并不完美(尤其是评估部分)-但我想自己尝试一下,看看是否可以复制jQuery动画功能,所以请原谅我的“坏”做法,请不要建议我使用jQuery,我想尝试一下。 2:这段代码还没有完成,我只是想找出是什么让它工作不好 所以动画运行了大约12秒,而我输入的持续时间参数是15秒,我做错了什么 function animate(elem, attr, duration){ if(attr.constructor === Object){//check for obj

首先我想提两件事,
One:我的代码并不完美(尤其是评估部分)-但我想自己尝试一下,看看是否可以复制jQuery动画功能,所以请原谅我的“坏”做法,请不要建议我使用jQuery,我想尝试一下。
2:这段代码还没有完成,我只是想找出是什么让它工作不好

所以动画运行了大约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);
函数动画(元素、属性、持续时间){
如果(attr.constructor==Object){//检查对象文字
var i=0;
var cssProp=[];
var cssValue=[];
for(输入属性){
cssProp[i]=键;
cssValue[i]=attr[key];
}
var fps=(1000/60);
var t=setInterval(函数(){

对于(var j=0;j您不能使用
setInterval
来获得精确的总计时。因为JS是单线程的,并且多个事物在一个线程上竞争周期,所以不能保证下一个interval调用将精确地准时进行,或者N个interval将消耗精确的持续时间

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

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

这是关于tweening(不断重新计算步骤以精确匹配持续时间的过程)


我还建议您查看一些库(jQuery、YUI或您找到的任何其他库)中的动画代码因为他们都可以向您展示如何以一种非常通用的方式来实现这一点,包括tweening、easing函数等等。

我不会尝试重构它并找出答案,因为它非常不可靠。这意味着……一些事情

不要依赖正在制作动画的值来了解动画进度

一般来说,你的方法是不可靠的。你最好自己跟踪进度。此外,由于你的方法,你的数学似乎太难了,应该简单得多

这样想:当时间过去时,动画就完成了,而不是当动画值似乎表明它位于最终位置时

不增加,设置

浮点数学是不精确的,像这样的重复加法累加也会累加浮点错误。让一些变量为您跟踪进度更具可读性,您可以在计算中使用这些变量

animatedValue += changeOnThisFrame // BAD!
animatedValue = valueOnThisFrame   // GOOD!
不要跳积极/消极的条件舞

事实证明,
10+10
10-(-10)
实际上是一样的。这意味着您可以随时添加值,但变化率可以是负的,也可以是正的,并且值将在适当的方向上进行动画

超时和间隔不准确

事实证明,
setTimeout(fn,50)
实际上意味着计划至少在50毫秒后调用fn。在这50毫秒后执行的下一个JS run循环将运行该函数,因此您不能指望它完全准确

也就是说,它通常在几毫秒之内。但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]
在这里,以及它代表了什么

简单示例

它的功能不如您现有的功能,但它显示了您应该采取的方法。它使用动画开始时间创建完美值,具体取决于动画的完成程度、开始位置和前进方向。它不使用当前动画值来确定任何内容,并保证运行完整的镜头动画的gth

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:

请先删除eval部分。使用
var style=document.getElementById(elem.style[cssProp[j]];
而不是那难看的str构建。首先,通过
eval
进行调试会非常痛苦。请先清理一下……作为提醒,
eval(“foo”(+str+))
foo(str)
相同,
eval(“obj.+propName”)
obj[propName]
相同。这两条规则应该允许删除那里几乎所有的eval。eval是邪恶的,使用a而不是iI。我已经编辑了它,现在没有eval……有人知道为什么会发生这种情况吗?
k是什么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);
​