假設有這樣一個動畫功能需求:把一個div的寬度從100px變化到200px。寫出來的程式碼可能是這樣的:
animate1(document.getElementById('test1'), 200, 1000);
有沒有更好的做法呢?以下先來看一道小學數學題:
這題帶來的啟發是,某個時刻的路程是可以透過特定公式計算出來的。同理,動畫過程中某個時刻的數值也可以透過公式計算出來,而不是累積得出:
var timerId = setInterval(function() {
var percentage = (new Date - startTime) / duration;
var stepValue = startValue (endValue - startValue) * percentage;
element.style.width = stepValue 'px';
if (percentage >= 1) {
clearInterval(timerId);
element.innerHTML = new Date - startTime;
}
}, 13);
}
animate2(document.getElementById('test2'), 200, 1000);
這樣改良之後,可以看到動畫執行耗時最多只會有10幾ms的誤差。但問題還沒完全解決,在瀏覽器開發工具中檢查test2元素可以發現,test2的最終寬度可能不只200px。仔細檢查animate2函數的程式碼可以發現:
1.percentage的值可能大於1,可以透過Math.min限制最大值來解決。
2.即使保證了percentage的值不大於1,只要endValue或startValue為小數,(endValue - startValue) * percentage的值也可能產生誤差,因為Javascript小數運算的精確度不夠。其實我們要確保的只是最終值的準確性,所以在percentage為1的時候,直接使用endValue即可。
於是,animate2函數的程式碼修改為:
var timerId = setInterval(function() {
// 保證百分率不大於1
var percentage = Math.min(1, (new Date - startTime) / duration);
var stepValue;
if (percentage >= 1) {
// 確保最終值的準確度
stepValue = endValue;
} else {
stepValue = startValue (endValue - startValue) * percentage;
}
element.style.width = stepValue 'px';
if (percentage >= 1) {
clearInterval(timerId);
element.innerHTML = new Date - startTime;
}
}, 13);
}
還有最後一個疑問:setInterval的間隔為何設為13ms?原因是當下顯示器的刷新率一般不超過75Hz(即每秒刷新75次,也就是每隔約13ms刷新一次),把間隔跟刷新率同步效果更好。