亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

制作60fps的高性能動畫

617035918 / 1721人閱讀

摘要:因為異步的關(guān)系中的回調(diào)函數(shù)并非立即執(zhí)行,而是需要加入等待隊列中。通知繪制位圖到屏幕上而就只需要繪制圖層了,所以硬件加速的性能無疑更好。

寫在前面

說到web的高性能動畫,這部分內(nèi)容其實已經(jīng)是老生常談的了,不過其中還是有不少比較新的而且非常實用的內(nèi)容可以和大家分享一下。
讀完這篇文章后相信大家都會對動畫渲染的機(jī)制以及制作60fps動畫的關(guān)鍵要素有足夠的理解,以后遇上了動畫相關(guān)的問題也可以很好的從源頭上解決。

正文 什么是高性能動畫呢?

動畫幀率可以作為衡量標(biāo)準(zhǔn),一般來說畫面在 60fps 的幀率下效果比較好。

換算一下就是,每一幀要在 16.7ms (16.7 = 60/1000) 內(nèi)完成渲染。因此,我們的首要任務(wù)是減少不必要的性能消耗。 越多的幀需要渲染的,意味著有越多的任務(wù)需要瀏覽器處理,所以掉幀就出現(xiàn)了,這是達(dá)到 60fps 的一個絆腳石。如果所有動畫都無法在 16.7ms 渲染完畢,不如考慮用略低的 30fps 幀率來渲染。

如何實現(xiàn)絲般順滑

這里主要決定因素有二:
時機(jī)(Frame Timing): 新的一幀準(zhǔn)備好的時機(jī)
成本(Frame Budget): 渲染新的一幀需要多長的時間

開始繪制的時機(jī)

一般來說我們使用setTimeout(callback, 1/60)來實現(xiàn)16.7ms后執(zhí)行動畫一幀的渲染。
然而setTimeout實際上并不準(zhǔn)確。
首先,setTimeout依靠瀏覽器內(nèi)置時鐘的更新頻率
例如:IE8及以前更新間隔為15.6ms,setTimeout(callback, 1/60)為16.7ms,那么它就需要兩個15.6ms才會觸發(fā),這也意味著無故延遲了 15.6 x 2 - 16.7 = 14.5毫秒。

其次,假使能夠達(dá)到16.7ms,它還要面臨一個異步隊列的問題。
因為異步的關(guān)系setTimeout中的回調(diào)函數(shù)并非立即執(zhí)行,而是需要加入等待隊列中。但問題是,如果在等待延遲觸發(fā)的過程中,有新的同步腳本需要執(zhí)行,那么同步腳本不會排在timer的回調(diào)之后,而是立即執(zhí)行。

function runForSeconds(s) {
    var start = +new Date();
    while (start + s * 1000 > (+new Date())) {}
}

document.body.addEventListener("click", function () {
    runForSeconds(10);
}, false);

setTimeout(function () {
    console.log("Done!");
}, 1000 * 3);

以上的例子是,如果在等待觸發(fā)延遲的3秒過程中,有人點(diǎn)擊了body,那么回調(diào)還是準(zhǔn)時在3s完成時觸發(fā)嗎?
實踐執(zhí)行的時候,它會等待10s,同步函數(shù)總是優(yōu)先于異步函數(shù)。

基于這些問題我們提出了另一個解決方案:requestAnimationFrame(callback)

window.requestAnimationFrame() 方法告訴瀏覽器您希望執(zhí)行動畫并請求瀏覽器在下一次重繪之前調(diào)用指定的函數(shù)來更新動畫。該方法使用一個回調(diào)函數(shù)作為參數(shù),這個回調(diào)函數(shù)會在瀏覽器重繪之前調(diào)用。-- MDN

當(dāng)我們調(diào)用這個函數(shù)的時候,我們告訴它需要做兩件事:

我們需要新的一幀;

當(dāng)你渲染新的一幀時需要執(zhí)行我傳給你的回調(diào)函數(shù)

與 setTimeout 相比,rAF(requestAnimationFrame) 最大的優(yōu)勢是由系統(tǒng)來決定回調(diào)函數(shù)的執(zhí)行時機(jī)。具體一點(diǎn)講就是,系統(tǒng)每次繪制之前會主動調(diào)用 rAF 中的回調(diào)函數(shù),如果系統(tǒng)繪制率是 60Hz,那么回調(diào)函數(shù)就每16.7ms 被執(zhí)行一次,如果繪制頻率是75Hz,那么這個間隔時間就變成了 1000/75=13.3ms。換句話說就是,rAF 的執(zhí)行步伐跟著系統(tǒng)的繪制頻率走。它能保證回調(diào)函數(shù)在屏幕每一次的繪制間隔中只被執(zhí)行一次(函數(shù)節(jié)流,這篇文章就不細(xì)說了,感興趣的可以查一下),這樣就不會引起丟幀現(xiàn)象,也不會導(dǎo)致動畫出現(xiàn)卡頓的問題。

另外它可以自動調(diào)節(jié)頻率。如果callback工作太多無法在一幀內(nèi)完成會自動降低為30fps。雖然降低了,但總比掉幀好。

同時對比使用 setTimeout 實現(xiàn)的動畫,當(dāng)頁面被隱藏或最小化時,setTimeout 仍然在后臺執(zhí)行動畫任務(wù),由于此時頁面處于不可見或不可用狀態(tài),刷新動畫是沒有意義的,而且還浪費(fèi) CPU 資源。而 rAF 則完全不同,當(dāng)頁面處理未激活的狀態(tài)下,該頁面的屏幕繪制任務(wù)也會被系統(tǒng)暫停,因此跟著系統(tǒng)步伐走的 rAF 也會停止渲染,當(dāng)頁面被激活時,動畫就從上次停留的地方繼續(xù)執(zhí)行,有效節(jié)省了 CPU 開銷。

對于rAF的兼容性問題其實已經(jīng)有了很好的處理方案了,以下是一種比較簡單的:

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

這種寫法沒有考慮 cancelAnimationFrame 的兼容性,并且不是所有的設(shè)備繪制時間間隔都是1000/60。
這個是比較不錯的polyfil 。

繪制一幀的時間

總的來說,rAF解決了前面的第一個問題(繪制時機(jī)),至于第二個問題(繪制成本),rAF是無能為力的,最多也就是采取自動降低頻率的方式處理。
這里就需要從瀏覽器渲染方面來優(yōu)化了,首先看下這個圖:

Rendering
頁面首次加載時,瀏覽器會下載并解析 HTML,將 HTML 元素轉(zhuǎn)變?yōu)橐粋€ DOM 節(jié)點(diǎn)的「內(nèi)容樹」(content tree)。除此之外,樣式同樣會被解析生成「渲染樹」 (render tree)。為了提升性能,渲染引擎會分開完成這些工作,甚至?xí)霈F(xiàn)渲染樹比 DOM 樹更快生成出來。

在這個階段里最影響繪制時間的自然就是Layout了

// animation loop
function update(timestamp) {
    for(var m = 0; m < movers.length; m++) {
        // DEMO 版本
        //movers[m].style.left = ((Math.sin(movers[m].offsetTop + timestamp/1000)+1) * 500) + "px";

        // FIXED 版本
        movers[m].style.left = ((Math.sin(m + timestamp/1000)+1) * 500) + "px";
        }
    rAF(update);
};
rAF(update);

上面例子里DEMO版本是非常慢的,之所以慢的原因是,在修改每一個物體的left值時,會請求這個物體的offsetTop值,觸發(fā)了重排,這是一個非常耗時的reflow操作。

通常我們會不知不覺中寫了很多的頻繁layout的代碼,例如:

var h1 = element1.clientHeight;
element1.style.height = (h1 * 2) + "px";

var h2 = element2.clientHeight; 
element2.style.height = (h2 * 2) + "px";

var h3 = element3.clientHeight;
element3.style.height = (h3 * 2) + "px";

不斷地讀寫 DOM 會導(dǎo)致「強(qiáng)制同步布局」(forced synchronous layouts),不過在技術(shù)發(fā)展過程中它演變成了更形象的詞 — 「布局抖動」(layout thrashing)(詳情可以看一下這篇文章 layout thrashing)。
瀏覽器會追蹤「臟元素」,在合適的時候?qū)⒆儞Q過程儲存起來,然后在讀取了特定屬性以后,開發(fā)者可以強(qiáng)制瀏覽器提前計算,這樣反復(fù)的讀寫會導(dǎo)致重排。
所以這里我們需要進(jìn)行優(yōu)化,先讀后寫就是一個解決方案,上面的代碼可以改寫為:

// Read
var h1 = element1.clientHeight;
var h2 = element2.clientHeight;
var h3 = element3.clientHeight;

// Write
element1.style.height = (h1 * 2) + "px";
element2.style.height = (h2 * 2) + "px";
element3.style.height = (h3 * 2) + "px";

當(dāng)然這種只能應(yīng)對一些普通的情況,如果代碼是解耦的或者更復(fù)雜的讀寫后嵌套讀寫操作的這些情況可以使用一些比較成熟的解決方案,例如fastdom.js。另外一個小技巧是使用rAF來延遲全部的寫操作到下一幀執(zhí)行也是很不錯的解決方案。

Paint
生成布局后,瀏覽器將頁面繪制到屏幕上。這個環(huán)節(jié)和前一個步驟類似,瀏覽器會追蹤臟元素,將它們合并到一個超大的矩形區(qū)域中。每一幀內(nèi)只會發(fā)生一次重繪,用于繪制這個被污染區(qū)域。

這個階段對性能的影響主要在于重繪。

減少不必的繪制

例如,gif圖即使不可見,也可能導(dǎo)致paint,不需要時應(yīng)將gif圖的display屬性設(shè)為none
在經(jīng)常paint的區(qū)域,要避免代價太高的style
代價比較高的樣式:

color,border-style,visibility,background,
text-decoration,background-image,
background-position,background-repeat
outline-color,outline,outline-style
border-radius,outline-width,box-shadow
background-size

參考網(wǎng)站:https://csstriggers.com/

減少繪制的區(qū)域

為引起大范圍Paint的元素生成獨(dú)立的Layer以減小Paint的范圍
可以參考一下這個demo網(wǎng)站,綠色部分為重繪區(qū)域:

Composite

將所有繪制好的元素進(jìn)行復(fù)合。
默認(rèn)情況下,所有元素將會被繪制到同一個層中,如果將元素分開到不同的復(fù)合層中,更新元素對性能友好,不在同一層的元素不容易受到影響。
這一階段里CPU 繪制層,GPU 生成層。GPU 復(fù)合層上的改變代價最小性能消耗最少。所以這里的優(yōu)化主要就是把代價高的改動都放到GPU上,也就是一般說的開啟硬件加速技術(shù),可以說有益無害,如果設(shè)備的性能足夠開啟就對了。
這里的限制主要有:GPC和CPU之間帶寬,GPU的限度。

這里需要區(qū)分一下CPU,GPU的工作:

CPU工作比較多,還分主線程和合成線程。
主線程主要負(fù)責(zé):

Javascript 的計算與執(zhí)行

CSS 樣式計算

Layout 計算

將頁面元素繪制成位圖(paint),也就是光柵化(Raster)

將位圖給合成線程

合成線程則主要負(fù)責(zé):

將位圖(GraphicsLayer 層)以紋理(texture) 的形式上傳給 GPU(GPC和CPU之間帶寬)

計算頁面的可見部分和即將可見部分(滾動)

CSS 動畫處理(CSS 動畫而言,由于其流程不受主線程的影響,所以性能更好。)

通知 GPU 繪制位圖到屏幕上

而GPU就只需要繪制圖層了,所以硬件加速的性能無疑更好。

開啟硬件加速的方式主要有:

通過改變 opacitytransform 的值觸發(fā)

通過transform的3D屬性強(qiáng)制開啟GPU加速

will-change顯式地通知瀏覽器對某一個元素的某個或某些元素做渲染優(yōu)化

硬件加速之后,瀏覽器會為此元素多帶帶創(chuàng)建一個“層”。當(dāng)有多帶帶的層之后,此元素的repaint操作將只需要更新自己,不用影響到別人。你可以將其理解為局部更新。所以開啟了硬件加速的動畫會變得流暢很多

默認(rèn)情況下,transform、opacity這類css屬性CPU是直接通知GPU來做處理的,因為GPU能快速對texture(紋理:CPU傳輸?shù)紾PU的一個Bitmap)進(jìn)行偏移、縮放、旋轉(zhuǎn)、修改透明度等操作,不經(jīng)過主線程的layout、paint過程。也就是開啟了硬件加速。

will-change是個新事物,它能夠顯式地通知瀏覽器對某一個元素的某個或某些元素做渲染優(yōu)化。 will-change 接收各種各樣的屬性值,比如一個或多個 CSS 屬性 (transform, opacity)、contents 或者 scroll-position。不過最常用值可能就是 auto,這個值表示的是瀏覽器將進(jìn)行默認(rèn)的優(yōu)化:

GPU雖然擅長處理圖像,但是它也有瓶頸。連接CPU和GPU之間的帶寬是有限的,如果一次更新的層太多,則很容易就達(dá)到GPU的瓶頸,影響到動畫的流暢度。所以我們需要控制層的數(shù)量和層paint的次數(shù)。

控制層的數(shù)量可以理解,因為層的創(chuàng)建和更新都會消耗內(nèi)存。而控制層paint的次數(shù),是為了減少位圖更新的次數(shù)。每次位圖更新,合成線程就需要提交新的位圖給GPU。頻繁地更新位圖也會拖慢GPU的效率。

優(yōu)化有度,我們總能聽到關(guān)于「復(fù)合層過多反而阻礙渲染」的討論。因為瀏覽器已經(jīng)為優(yōu)化做了能做的一切, will-change 的性能優(yōu)化方案本身對資源要求很高。如果瀏覽器持續(xù)在執(zhí)行某個元素的 will-change,就意味著瀏覽器要持續(xù)對這個元素的進(jìn)行優(yōu)化,性能消耗造成頁面卡頓。過多的復(fù)合層降低頁面性能的現(xiàn)象在移動端很常見。

避免意外生成的layer
z-index高于Layer的元素,也會生成多帶帶的Layer
demo以及說明頁面

小結(jié)

實現(xiàn)絲般順滑主要決定因素有二:
時機(jī)(Frame Timing):

rAF

成本(Frame Budget):

避免layout:先讀后寫

盡量少paint:注意樣式的使用

適當(dāng)?shù)挠布铀?/p>

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/97322.html

相關(guān)文章

  • 移動端滾動研究

    摘要:還會有一個性能上的問題就是當(dāng)頁面的列表過長,元素過多時,在模擬滾動,下拉刷新這段時間內(nèi),頁面也會有卡頓現(xiàn)象,這里采取了一個優(yōu)化策略即列表較長時數(shù)量較多時,在觸發(fā)下拉刷新的時機(jī)時將頁面視窗之外的元素隱藏或者存放在里面。 移動web滾動問題 在移動端如果使用局部滾動,意思就是我們的滾動在一個固定寬高的div內(nèi)觸發(fā),將該div設(shè)置成overflow:scroll/auto;來形成div內(nèi)部的...

    ghnor 評論0 收藏0
  • 前端動畫技術(shù)研究和比較

    摘要:它和前端動畫之間沒有包含與被包含的關(guān)系,更不能將它們混為一談,只有兩者的有機(jī)結(jié)合才能創(chuàng)建出炫酷的界面。 第一次在segmentfault上發(fā)文章 :),歡迎評論指正。原文最初發(fā)表在 https://github.com/WarpPrism/... 動畫相關(guān)概念 幀:動畫過程中每一個靜止的狀態(tài),每一張靜止的圖片 幀率:刷新頻率,每秒鐘播放的幀數(shù),F(xiàn)PS(frame per second...

    wushuiyong 評論0 收藏0
  • 前端動畫技術(shù)研究和比較

    摘要:它和前端動畫之間沒有包含與被包含的關(guān)系,更不能將它們混為一談,只有兩者的有機(jī)結(jié)合才能創(chuàng)建出炫酷的界面。 第一次在segmentfault上發(fā)文章 :),歡迎評論指正。原文最初發(fā)表在 https://github.com/WarpPrism/... 動畫相關(guān)概念 幀:動畫過程中每一個靜止的狀態(tài),每一張靜止的圖片 幀率:刷新頻率,每秒鐘播放的幀數(shù),F(xiàn)PS(frame per second...

    endiat 評論0 收藏0
  • 網(wǎng)頁性能分析不完全指南

    摘要:因此,如果可能,最好利用好毫秒響應(yīng)預(yù)先計算開銷大的工作,這樣網(wǎng)站就更有可能實現(xiàn)的性能??臻e主線程工作分成不大于毫秒的塊。點(diǎn)擊按鈕見圖示,會在頁面運(yùn)行時捕獲性能指標(biāo)。 前言 經(jīng)常能在博客或者論壇上看到很多有關(guān)前端性能優(yōu)化的文章,但是卻很少看到如何分析一個網(wǎng)頁的性能的文章。到底什么樣的指標(biāo)(或者說是標(biāo)準(zhǔn))代表這個網(wǎng)頁性能好或者不好,通過什么方式來得到這些指標(biāo)呢?因此,本文來講述下如何分析一...

    zgbgx 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<