業(yè)界動(dòng)畫引擎
PhysicsJS: 基于JavaScript仍劈、模塊化娄猫、可擴(kuò)展、易于使用的物理引擎外冀,github
animate.css: CSS3動(dòng)畫寡键,github
Matter.js: 基于canvas,兼容IE9+雪隧,github
collie:基于Canvas西轩,IE9+
FPS
動(dòng)畫間隔決定了動(dòng)畫的每秒幀數(shù)(FPS), 一般來說,F(xiàn)PS越高脑沿,也就是每秒播放的幀數(shù)越多藕畔,動(dòng)畫會(huì)越流暢,但是庄拇,因?yàn)榇蟛糠值娘@示器刷新頻率是 60Hz注服,當(dāng)動(dòng)畫的FPS超過 60Hz 時(shí),顯示器會(huì)把兩個(gè)或更多的幀顯示在同一畫面上措近,這樣就會(huì)出現(xiàn) 畫面撕裂溶弟,畫面撕裂跟掉幀一個(gè)意思,所以通常來講 FPS 為 60frame/s 時(shí)動(dòng)畫效果最好熄诡,也就是每幀16.67ms可很,在瀏覽器中要減去渲染時(shí)間1ms左右,得到的結(jié)果是每幀時(shí)間大概15ms凰浮。但是我們可以在 jQuery 的源碼中發(fā)現(xiàn)它的 interval是13ms:jQuery.fx.interval = 13我抠, 按照上面的說法:1000 /(13+1.5)= 70 > 60hz苇本,這樣會(huì)出現(xiàn)畫面撕裂,作為業(yè)界標(biāo)準(zhǔn)的 jQuery 顯然是不會(huì)出現(xiàn)這種低級(jí)錯(cuò)誤的菜拓,所以2ms的差別是怎么個(gè)意思瓣窄?John Resig有一篇博客對(duì)13ms做了解釋,文章鏈接見末尾纳鼎。因?yàn)閖Query的動(dòng)畫是基于 setInterval
的俺夕,所以會(huì)存在一定的延遲:setTimeout(func, delay),這里是說在delay時(shí)間后將任務(wù) func加入 UI 任務(wù)隊(duì)列贱鄙,而非立即執(zhí)行該任務(wù)劝贸,所以這里會(huì)有一定的延遲;各瀏覽器定時(shí)器精度的差異逗宁。
對(duì)于 setInterval的問題映九,新的方法 requestAnimationFrame
是很好的改進(jìn),具體可參見The secret to silky smooth JavaScript animation!這篇文章瞎颗。
參考:
stackoverflow
jQuery 的動(dòng)畫幀寬為什么是 13ms 呢
使用requestAnimationFrame更好的實(shí)現(xiàn)javascript動(dòng)畫
requestAnimationFrame:
setInterval和 setTimeout的缺陷*
無論是setInterval()還是setTimeout()都無法達(dá)到精確件甥,這個(gè)延遲即你指定的第二個(gè)參數(shù)僅僅表示何時(shí)代碼會(huì)添加到瀏覽器的可能被執(zhí)行的UI線程隊(duì)列中。如果隊(duì)列中有其他工作在此之前哼拔,那代碼將會(huì)等到他完成才會(huì)執(zhí)行引有。簡(jiǎn)而言之倦逐,毫秒級(jí)的延遲不是表示何時(shí)代碼會(huì)執(zhí)行,而是表示何時(shí)代碼會(huì)添加進(jìn)隊(duì)列檬姥;
當(dāng)相應(yīng)的瀏覽器窗口最小化,JavaScript 計(jì)時(shí)器在背景標(biāo)簽仍然持續(xù)運(yùn)行穿铆,消耗CPU和電池。
CSS transitions和 animations
優(yōu)勢(shì)在于瀏覽器知道哪些動(dòng)畫將會(huì)發(fā)生斋荞,所以得到正確的間隔來刷新UI荞雏。而javascript動(dòng)畫,瀏覽器不知道動(dòng)畫正在發(fā)生平酿,所以催生了requestAnimationFrame凤优,對(duì)于延遲做了很大程度的優(yōu)化。
僅繪制用戶可見的動(dòng)畫蜈彼,這意味著在頁面不可見時(shí)不會(huì)繪制動(dòng)畫筑辨,節(jié)省 CPU 和電池;繪制動(dòng)畫不可能出現(xiàn)多個(gè)排隊(duì)的回調(diào)函數(shù)幸逆,或者阻塞瀏覽器;由于瀏覽器準(zhǔn)備好時(shí)(空閑時(shí))才繪制幀棍辕,不會(huì)有等待繪制的幀暮现,沒有多余的幀繪制,因此動(dòng)畫更平滑楚昭,CPU 和電池使用被進(jìn)一步優(yōu)化栖袋。
注意:在有多個(gè)動(dòng)畫時(shí),出現(xiàn)一個(gè)可見一個(gè)不可見抚太,requestAnimationFrame 會(huì)導(dǎo)致動(dòng)畫不同步塘幅,所以, 指定一個(gè)參數(shù)確保所有需要同步的動(dòng)畫狀態(tài)尿贫,不受可見程度的影響(如一組動(dòng)畫從開始以來經(jīng)過的時(shí)間)电媳,而不是根據(jù)每個(gè)動(dòng)畫的前一幀。
兼容各瀏覽器的requestAnimationFrame
(function() {
var lastTime = 0;
var vendors = ['webkit', 'moz' /*, 'ms', 'o'*/];
for(var x = 0,len = vendors.length ; x < len && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || // name has changed in Webkit
window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
var id = window.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
}());
/* 方案2:*/
var oldStyleMove = (function() {
var timeLast = 0
return function( callback ) {
var timeCurrent = +new Date(),
timeDelta
timeDelta = Math.max( 0, 16 - ( timeCurrent - timeLast ))
timeLast = timeCurrent + timeDelta
return setTimeout( function() {
callback( timeCurrent + timeDelta )
}, timeDelta )
}
})(),
WIN = window,
requestAnimationFrame = WIN.requestAnimationFrame ||
WIN.webkitRequestAnimationFrame ||
WIN.mozRequestAnimationFrame ||
oldStyleMove,
cancelAnimationFrame = WIN.cancelAnimationFrame ||
WIN.webkitCancelAnimationFrame ||
WIN.mozCancelAnimationFrame ||
function( timeoutID ) {
clearTimeout( timeoutID )
};