首先我想提两点,
一:我的代码是不完美(esspechially的EVAL部分) -但我想尝试一些我自己,看看我是否能复制jQuery的动画功能,所以请原谅我的“坏”的做法,请不要建议我将使用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变量)的时间间隔。
在此先感谢 。
我不会试图重构这一点,数字出来,因为它是相当靠不住的。 这就是说......几件事情。
不要依赖于您设置动画的值,让你知道动画进度
一般来说你的做法是不合理的。 你是断保持跟踪的进步自己更好。 此外,由于你的方法的结果是你的数学好像它试图太硬了,应该是简单得多。
想想看这样的:你的动画完成时,时间已经过去,而不是当动画值似乎表明,它是在最后的位置。
不要增加,设置
浮点运算是不精确的,像这样反复此外累积会累加浮点错误也是如此。 而且它更具可读性,使一些变量来跟踪进度适合你,你可以在计算中使用。
animatedValue += changeOnThisFrame // BAD!
animatedValue = valueOnThisFrame // GOOD!
不要做正/负有条件的舞蹈
事实证明, 10 + 10
和10 - (-10)
是真正的同样的事情。 这意味着你可以随时添加值,但变化的速度可正可负,和值将在适当的方向动画。
超时和间隔不准确
原来setTimeout(fn, 50)
实际上意味着调度FN要晚至少50ms的调用。 接下来的JS运行循环执行后的50ms的运行的功能,所以你不能依赖它是完全正确的。
这就是说它通常是在几毫秒内。 但是60fps的帧16毫秒左右,并且该定时器实际上可能触发在从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/
不能使用setInterval
精确计时总额。 因为JS是单线程和多件事情争夺一个线程上循环,存在不保证下一时间间隔呼叫将准时或N个间隔要花费不少时间的确切持续时间。
取而代之的是,几乎所有的动画程序获取当前时间和使用系统时钟来测量时间为总时间。 一般算法是获取开始时间,计算期望的结束时刻(开始时间+持续时间)。 然后,当你做了,迭代计算的预期步进值和号码。 然后,在每一个步骤,您重新计算左剩余时间和剩余步长值。 通过这种方式,可以确保动画总是准确地完成(或几乎完全一致)的时间和你总是得到完全的最终位置。 如果动画得到理想轨迹的背后,则它会自我纠正和移动略多的剩余步骤。 如果它获得领先以任何理由(舍入误差,等...),它将回拨步长,同样在时间上的最终位置到达。
您可能还需要知道浏览器不支持总是非常小的时间量。 每个浏览器都有某种形式的最短时间,他们将允许定时器操作。 下面是文章的最小定时器水平。
下面是文章的渐变(的不断重新计算步骤,从而精确匹配的时间过程)。
我还建议你看一下代码在一些图书馆(jQuery的,YUI或者你发现其他任何一个)做动画,因为它们都可以告诉你这是如何在一个非常通用的方式进行,包括补间,缓动函数等...