欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

緩動(dòng)函數(shù)requestAnimationFrame 更好的實(shí)現(xiàn)瀏覽器經(jīng)動(dòng)畫

 更新時(shí)間:2012年12月07日 11:21:03   作者:  
requestAnimationFrame是什么?一直是我們大家所疑惑的,緩動(dòng)函數(shù)requestAnimationFrame 更好的實(shí)現(xiàn)瀏覽器經(jīng)動(dòng)畫,接下來(lái)將為大家詳細(xì)介紹

寫緩動(dòng)函數(shù)用到requestAnimationFrame函數(shù),之前了解過(guò)一些,但總覺(jué)得又不是很了解,所以翻譯一篇老外的文章,以便學(xué)習(xí)分享。

requestAnimationFrame是什么?
以前我們做動(dòng)畫需要一個(gè)定時(shí)器,每間隔多少毫秒就做出一些改變?,F(xiàn)在有個(gè)好消息:瀏覽器廠商已經(jīng)決定提供一個(gè)專門做動(dòng)畫的方法,即requestAnimationFrame(),而且基于瀏覽器的層面也能更好的進(jìn)行優(yōu)化。但是呢,這只是一個(gè)做動(dòng)畫的基礎(chǔ)API,即不基于DOM元素的style變化,也不基于canvas,或者WebGL。所以,具體的動(dòng)畫細(xì)節(jié)需要我們自己寫。

我們?yōu)槭裁匆盟?/STRONG>
對(duì)于同時(shí)進(jìn)行的n個(gè)動(dòng)畫,瀏覽器能夠進(jìn)行優(yōu)化,把原本需要N次reflow和repaint優(yōu)化成1次,這樣就實(shí)現(xiàn)了高質(zhì)量的動(dòng)畫。舉個(gè)例子,現(xiàn)在有基于JS的動(dòng)畫,還有基于CSS的transitions,或者SVG SMIL. Plus,如果瀏覽器的某個(gè)tab正在運(yùn)行這樣一個(gè)動(dòng)畫,然后你切到另一個(gè)tab,或者干脆最小化,總之就是你看不見(jiàn)它了,這時(shí)瀏覽器就會(huì)停止動(dòng)畫。這將意味著更少的CPU,GPU和更少的內(nèi)存消耗,這樣電池的使用壽命就大大延長(zhǎng)了。

如何使用它?

復(fù)制代碼 代碼如下:

// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback, /* DOMElement */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
// usage:
// instead of setInterval(render, 16) ....
(function animloop(){
render();
requestAnimFrame(animloop, element);
})();

注意:這里我使用了“requestAnimFrame”,因?yàn)橐?guī)范仍在不斷變化中,我并不想任由規(guī)范擺布。
requestAnimationFrame API
復(fù)制代碼 代碼如下:

window.requestAnimationFrame(function(/* time */ time){
// time ~= +new Date // the unix time
}, /* optional bounding elem */ elem);

先給出Chrome和Firefox的版本
復(fù)制代碼 代碼如下:

window.mozRequestAnimationFrame([callback]); // Firefox
window.webkitRequestAnimationFrame(callback[, element]); // Chrome

參數(shù):
callback:(FF可選,Chrome必選)
  下次repaint調(diào)用的函數(shù),函數(shù)的第一個(gè)參數(shù)是當(dāng)前時(shí)間
element:(FF無(wú))
  意譯一下吧:其實(shí)就是畫布了,而那個(gè)‘畫',是動(dòng)畫。(the element that visually bounds the entire animation)。對(duì)canvas和WebGL來(lái)說(shuō),它就是<canvas>元素,對(duì)于DOM節(jié)點(diǎn)來(lái)說(shuō),你可以不管它,如果你想稍微進(jìn)行一下優(yōu)化,也可以傳個(gè)參數(shù)進(jìn)來(lái)。

它到底靠不靠譜???
現(xiàn)在,Webkit實(shí)現(xiàn)(Nightly Safari 和 Chrome Dev Channel 可用)和Mozilla實(shí)現(xiàn)(FF4可用)有一些的差異,Mozilla的實(shí)現(xiàn)有一個(gè)Bug。事實(shí)上,F(xiàn)F動(dòng)畫的幀數(shù)是這么算的:1000/(16 + N) fps,其中N是callback的執(zhí)行時(shí)間,單位為毫秒。如果你的callback執(zhí)行時(shí)間為1000ms,那么它最高的幀數(shù)也就只有1fps。如果你的callback執(zhí)行時(shí)間為1ms,那么幀數(shù)差不多就是60fps。這個(gè)bug肯定會(huì)被修復(fù),也許就是FF4的下一個(gè)版本吧。Chrome10沒(méi)有time參數(shù)(added in m11.弱弱的問(wèn)下,m11是什么?),F(xiàn)F目前沒(méi)有element參數(shù)。
我看了下火狐的那個(gè)bug,大概就是說(shuō):
FF的mozRequestAnimationFrame()永遠(yuǎn)不可能達(dá)到60fps,即使你的callback執(zhí)行時(shí)間小于1000/60毫秒。舉個(gè)例子:
復(fù)制代碼 代碼如下:

function callback(time) {
window.mozRequestAnimationFrame(callback);
doWork();
}

如果doWork()耗時(shí)1000/60毫秒,那么幀數(shù)大約是30fps,而同樣的動(dòng)畫如果使用setTimeout(callback, 16),幀速則是60fps。似乎callback總是在callback執(zhí)行完畢后的大約16ms再次開始執(zhí)行,而不是在callback開始執(zhí)行后的16ms再次開始執(zhí)行,如果是后者,且計(jì)算又夠快的話,就能產(chǎn)生60fps的幀數(shù)。
如果你是規(guī)范控,傳送門在此



話不多說(shuō),首先來(lái)個(gè)經(jīng)典的動(dòng)畫函數(shù)
復(fù)制代碼 代碼如下:

function animate(element, name, from, to, time) {
time = time || 800; //默認(rèn)0.8秒
var style = element.style,
latency = 60, // 每60ms一次變化
count = time / latency, //變化的次數(shù)
step = Math.round((to - from) / count), //每一步的變化量
now = from;
function go() {
count--;
now = count ? now + step : to;
style[name] = now + 'px';
if (count) {
setTimeout(go, latency);
}
}
style[name] = from + 'px';
setTimeout(go, latency);
}

姑且不論這個(gè)函數(shù)的設(shè)計(jì)存在局限性,如只能對(duì)以px為單位的樣式進(jìn)行修改。僅從函數(shù)的實(shí)現(xiàn)上來(lái)看,這可以是一個(gè)非常經(jīng)典的動(dòng)畫理念,其基本邏輯由以下部分組成:
獲取起點(diǎn)值from和終點(diǎn)值to,通過(guò)動(dòng)畫需要進(jìn)行的時(shí)間time,以及每偵間隔latency的要求,計(jì)算出值的改變次數(shù)count和每次改變的量step。
開啟setTimeout(fn, latency);來(lái)步進(jìn)到下一偵。

在下一偵中,設(shè)置屬性步進(jìn)一次,如果動(dòng)畫還沒(méi)結(jié)束,再回到第2步繼續(xù)下一偵。
這個(gè)函數(shù)工作得很好,服務(wù)了千千萬(wàn)萬(wàn)的站點(diǎn)和系統(tǒng),事實(shí)上jQuery的animate函數(shù)的核心也無(wú)非是setInterval函數(shù)。
但是,隨著現(xiàn)在系統(tǒng)復(fù)雜度的穩(wěn)步上升,動(dòng)畫效果也越來(lái)越多,同時(shí)對(duì)動(dòng)畫的流暢度也有了更多的重視,這導(dǎo)致上面的函數(shù)會(huì)出現(xiàn)一些問(wèn)題。例如同時(shí)打開100個(gè)動(dòng)畫效果,根據(jù)上面的函數(shù),很明顯會(huì)有100個(gè)定時(shí)器在同時(shí)運(yùn)行,這些定時(shí)器之間的調(diào)度會(huì)對(duì)性能有輕微的影響。雖然在正常的環(huán)境中,這些許的影響并不會(huì)有什么關(guān)系,但是在動(dòng)畫這種對(duì)流暢度有很高要求的環(huán)境下,任何細(xì)微的影響都可能產(chǎn)生出不好的用戶體驗(yàn)。

在這樣的情況下,有一些開發(fā)者就發(fā)明了一種基于統(tǒng)一幀管理的動(dòng)畫框架,他使用一個(gè)定時(shí)器觸發(fā)動(dòng)畫幀,不同的動(dòng)畫來(lái)注冊(cè)這些幀,在每一幀上處理多個(gè)動(dòng)畫的屬性變化。這樣的好處是減少了定時(shí)器調(diào)度的開銷,但是對(duì)于動(dòng)畫框架的開發(fā)者來(lái)說(shuō),統(tǒng)一幀管理、提供監(jiān)聽(tīng)?zhēng)腁PI等,都是需要開發(fā)和維護(hù)的。

瀏覽器的直接支持
最終,瀏覽器廠商們發(fā)現(xiàn)這件事其實(shí)可以由他們來(lái)做,并且基于瀏覽器層面,還可以有更多的優(yōu)化,比如:
對(duì)于一個(gè)偵中對(duì)DOM的所有操作,只進(jìn)行一次Layout和Paint。
如果發(fā)生動(dòng)畫的元素被隱藏了,那么就不再去Paint。
于是,瀏覽器開始推出一個(gè)API,叫做requestAnimationFrame,關(guān)于這個(gè)函數(shù),MDC的相關(guān)頁(yè)面有比較詳細(xì)的介紹,簡(jiǎn)單來(lái)說(shuō),這個(gè)函數(shù)有2種使用方法:
調(diào)用requestAnimationFrame函數(shù),傳遞一個(gè)callback參數(shù),則在下一個(gè)動(dòng)畫幀時(shí),會(huì)調(diào)用callback。
不傳遞參數(shù)地直接調(diào)用該函數(shù),啟動(dòng)動(dòng)畫幀,下一個(gè)幀觸發(fā)時(shí),會(huì)同時(shí)觸發(fā)window.onmozbeforepaint事件,可以通過(guò)注冊(cè)該事件來(lái)進(jìn)行動(dòng)畫。

第2種方法由于依賴于Firefox自己的事件,且beforepaint事件還沒(méi)進(jìn)入到標(biāo)準(zhǔn)中,所以不推薦使用,還是使用第1種方式比較好。此時(shí),我們的動(dòng)畫邏輯可以變成這樣:
記錄當(dāng)前時(shí)間startTime,作為動(dòng)畫開始的時(shí)間。
請(qǐng)求下一幀,帶上回調(diào)函數(shù)。
下一幀觸發(fā)時(shí),回調(diào)函數(shù)的第一個(gè)參數(shù)為當(dāng)前的時(shí)間,再與startTime進(jìn)行比較,確定時(shí)間間隔ellapseTime。
判斷ellapseTime是否已經(jīng)超過(guò)事先設(shè)定的動(dòng)畫時(shí)間time,如果超過(guò),則結(jié)束動(dòng)畫。
計(jì)算動(dòng)畫屬性變化的差值differ = to - from,再確定在ellapseTime的時(shí)候應(yīng)該變化多少step = differ / time * ellapseTime。
計(jì)算出現(xiàn)在應(yīng)該變化到的位置Math.round(from + step),并重新對(duì)樣式賦值。
繼續(xù)請(qǐng)求下一幀。

新的動(dòng)畫函數(shù)
下面就是一個(gè)全新的動(dòng)畫函數(shù):
復(fù)制代碼 代碼如下:

function animate(element, name, from, to, time) {
time = time || 800; // 默認(rèn)0.8秒
var style = element.style,
startTime = new Date;
function go(timestamp) {
var progress = timestamp - startTime;
if (progress >= duration) {
style[name] = to + 'px';
return;
}
var now = (to - from) * (progress / duration);
style[name] = now.toFixed() + 'px';
requestAnimationFrame(go);
}
style[name] = from + 'px';
requestAnimationFrame(go);
}

到這一步,還剩一個(gè)問(wèn)題,那就是并不是每個(gè)瀏覽器都支持requestAnimationFrame函數(shù)的,所以再做一個(gè)簡(jiǎn)單的修正。
根據(jù)Firefox的特性來(lái)看,其mozRequestAnimationFrame提供的最高FPS為60,并且會(huì)根據(jù)每一幀的計(jì)算的耗時(shí)來(lái)進(jìn)行調(diào)整,比如每一幀計(jì)算用了1s,那他只會(huì)提供1FPS的動(dòng)畫效果。
而Chrome的高版本同樣也實(shí)現(xiàn)了這個(gè)函數(shù),叫webkitRequestAnimationFrame,可以預(yù)見(jiàn)未來(lái)還會(huì)有Opera的oRequestAnimationFrame和IE的msRequestAnimationFrame,所以這里一并做一個(gè)簡(jiǎn)單的兼容處理:
復(fù)制代碼 代碼如下:

requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function(callback) { setTimeout(callback, 1000 / 60); };

相關(guān)文章

最新評(píng)論