動畫作多了,天然就要考慮性能,我打算出一個系列的日誌,詳細的講解一下網頁動畫性能相關的知識,若是你已經能夠運用css3 canvas來作動畫,能夠來參考一下。css
<!--more-->
目前我作的最複雜的動畫就是360搜索中PC端的天氣動畫。html
<img src="https://static.imwineki.cn/st...; width="300" height="200">css3
共包含14個動畫場景,每一個場景基本由1-3個獨立的動畫疊加而成,抽象動畫共12個,從開發到優化完成共分爲四期完成,一二期全部的動畫均使用canvas完成,三期四期對動畫性能進行大幅度改進,重構了部分代碼。其中兼容低倍屏、高倍屏,canvas繪製的折線圖瀏覽器兼容到IE6(具體實現參見:基於canvas折線圖統計圖),動畫兼容到IE9,低版本瀏覽器動畫顯示靜態圖片。因此如下我全部分析均依賴於該項目。git
首先咱們先來簡單的介紹一下動畫原理,其實動畫自己是不動的,它的實現原理是利用人眼的「視覺暫留」現象,在段時間內連續播放數副精緻的畫面,使肉眼銀視覺殘象產生錯覺,而產生「動」的概念。github
相關概念
<li>幀 (Frame):在動畫過程當中,每一幅靜止畫面即爲一「幀」。web
<li>幀率(Frame per second):即每秒鐘播放的靜止畫面的數量,單位是fps(Frame per second)。chrome
<li>幀時長:每一幅靜止畫面的停留時間,單位通常是ms(毫秒)。canvas
<li> 跳幀(掉幀/丟幀):在幀率固定的動畫中,某一幀的時長遠高於平均幀時長,致使其後續數幀被擠壓而丟失的現象。segmentfault
正常狀況下瀏覽器渲染刷新頻率穩定在60fps左右,人眼是能夠看到流暢平滑的動畫的,通常來說低於30fps的動畫,就會有卡頓。瀏覽器
來了解一下,怎樣使用chrome這把利刃。
FPS Meter
使用這個工具你能夠檢測當前瀏覽器GPU渲染動畫的幀率。
<img src="https://static.imwineki.cn/st...; width=100 height=80>
固然以上只能在chrome中進行調試,若是你想在其餘的瀏覽器FPS的檢測,你可使用stats.js,偵聽全局或指定位置的幀率,JS實現,全部瀏覽器可用。
Timeline
具體使用方式能夠參考:chrome官方文檔
profiles
taskmanager
各廠出品的瀏覽器所用的渲染引擎不盡相同: IE使用Trident ,FireFox使用Gecko ,Safari使用WebKit ,Chrome 28+ 和 Opera 15+使用的是Blink(WebKit的分支)
現代的瀏覽器一般會有兩個重要的執行線程,這2個線程協同工做來渲染一個網頁:
<li>主線程
<li>合成線程
通常狀況下,主線程負責:
<li>運行js
<li>計算HTML元素的css樣式
<li>頁面佈局layout
<li>將元素繪製到一個或多個位圖中
<li>將這些位圖交給合成線程
合成線程負責:
<li>經過GPU將位圖渲染到屏幕
<li>通知主線程更新頁面中可見或即將變成可見的部分的位圖
<li>計算頁面中可見部分
<li>計算出當你在滾動頁面時哪部分是即將變成可見的
<li>當你滾動頁面時將相應位置的元素移動到可視區域
長時間執行 JavaScript 或渲染一個很大的元素會阻塞主線程,在這期間,它將沒法響應用戶的交互。
相反,合成線程則會盡可能去響應用戶的交互。當一個頁面發生變化時,因爲當今大多數設備的屏幕刷新率都是 60次/秒,因此合成線程也會以每秒60 幀的間隔去不斷重繪這個頁面,即便這個頁面不完整。
當用戶滾動頁面時,合成線程會通知主線程更新頁面中最新可見部分的位圖。可是,若是主線程響應地不夠快,合成線程不會保持等待,而是立刻繪製已經生成的位圖,還沒準備好的部分用白色進行填充。
也就是說js是單線程的,但瀏覽器是多線程的,感興趣的小夥伴能夠看一下我以前翻譯的谷歌日誌瀏覽器多進程架構。
大多數設備的屏幕刷新率都是 60次/秒,瀏覽器對每一幀畫面的渲染工做須要在16毫秒(1秒 / 60 = 16.66毫秒)以內完成。但實際上,在渲染某一幀畫面的同時,瀏覽器還有一些額外的工做要作(好比渲染隊列的管理,渲染線程與其餘線程之間的切換等等)。所以單純的渲染工做,通常須要控制在10毫秒以內完成,才能達到流暢的視覺效果。若是超過了這個時間限度,頁面的渲染就會出現卡頓效果,也就是常說的jank,它是很糟糕的用戶體驗。
在web頁面中,代碼就是通過大概如下步驟轉換成屏幕上的顯示像素:
1.JavaScript:通常來講,咱們會使用JavaScript來實現一些視覺變化的效果。
2.計算樣式:這個過程是根據CSS選擇器,好比.headline或.nav > .nav_item,對每一個DOM元素匹配對應的CSS樣式。這一步結束以後,就肯定了每一個DOM元素上該應用什麼CSS樣式規則。
3.佈局:上一步肯定了每一個DOM元素的樣式規則,這一步就是具體計算每一個DOM元素最終在屏幕上顯示的大小和位置。web頁面中元素的佈局是相對的,所以一個元素的佈局發生變化,會聯動地引起其餘元素的佈局發生變化。好比,<body>元素的寬度的變化會影響其子元素的寬度,其子元素寬度的變化也會繼續對其孫子元素產生影響。所以對於瀏覽器來講,佈局過程是常常發生的。
4.繪製:本質上就是填充像素的過程。包括繪製文字、顏色、圖像、邊框和陰影等,也就是一個DOM元素全部的可視效果。通常來講,這個繪製過程是在多個層上完成的。
5.渲染層合:由上一步可知,對頁面中DOM元素的繪製是在多個層上進行的。在每一個層上完成繪製過程以後,瀏覽器會將全部層按照合理的順序合併成一個圖層,而後顯示在屏幕上。對於有位置重疊的元素的頁面,這個過程尤爲重要,由於一旦圖層的合併順序出錯,將會致使元素顯示異常。
上述過程的每一步中都有發生jank的可能,所以必定要弄清楚你的代碼將會運行在哪一步。
雖然在理論上,頁面的每一幀都是通過上述的流水線處理以後渲染出來的,但並不意味着頁面每一幀的渲染都須要通過上述五個步驟的處理。實際上,對視覺變化效果的一個幀的渲染,有這麼三種 經常使用的 流水線:
1.JS / CSS > 計算樣式 > 佈局 > 繪製 > 渲染層合併
若是你修改一個DOM元素的」layout」屬性,也就是改變了元素的樣式(好比寬度、高度或者位置等),那麼瀏覽器會檢查哪些元素須要從新佈局,而後對頁面激發一個reflow過程完成從新佈局。被reflow的元素,接下來也會激發繪製過程,最後激發渲染層合併過程,生成最後的畫面。
2.JS / CSS > 計算樣式 > 繪製 > 渲染層合併
若是你修改一個DOM元素的「paint only」屬性,好比背景圖片、文字顏色或陰影等,這些屬性不會影響頁面的佈局,所以瀏覽器會在完成樣式計算以後,跳過佈局過程,只作繪製和渲染層合併過程。
3.JS / CSS > 計算樣式 > 渲染層合併
若是你修改一個非樣式且非繪製的CSS屬性,那麼瀏覽器會在完成樣式計算以後,跳過佈局和繪製的過程,直接作渲染層合併。第三種的性能最爲理想,通常來講對於動畫和滾動這種複合很重的渲染,咱們就儘可能向第三種靠攏。
性能優化是一門減法藝術,咱們要本着經歷簡化頁面徐然過程,而後使每一步的渲染儘量高效。
咱們先來講說GPU,大多數手機、 平板電腦、 和計算機都配備了GPU芯片,GPU有着很是專業的定位,這意味着GPU很是擅長作某些事情(好比繪圖),但在其餘方面則沒什麼優點。
如今,咱們來看看CPU和GPU的內部特色
CPU(Central Processing Unit),GPU(Graphics Processing Unit)翻譯過來,第一個叫作中央處理器,後者叫作視覺處理器,換言之,一樣是計算機中用於計算的核心組件,CPU主要負責通用計算,GPU主要負責專用計算。咱們來看看下面的這個圖理解一下:
翻譯一下:
<li>_強大的ALU - 下降操做延時_
<li>_巨大的緩存器 -
將長延遲內存訪問轉換爲短延遲緩存訪問_
<li>_複雜的控制器 -分支預測用於減小分支延遲 -數據轉發,減小數據延遲
_
翻譯一下:
<li>_小緩存 -用來提升內存吞吐量_
<li>_小控制器 -沒有分支預測,沒有數據轉發_
<li>_高效能ALU - 不少長延時,可是有大量的吞吐流量_
能夠看到,CPU中包含Control(控制層),Cache(緩存層),ALU(算術邏輯單元),其中ALU是主要負責進行簡單運算的。而GPU中則能夠明顯的看到包含大量的ALU模塊和少許的Cache和Control模塊。
算術邏輯單元(英語:Arithmetic Logic Unit, ALU)[1]是中央處理器的執行單元,是全部中央處理器的核心組成部分,由及閘和或閘構成的算數邏輯單元,主要功能是進行二進制的算術運算,如加減乘(不包括整數除法)。基本上,在全部現代CPU體系結構中,二進制都以二補數的形式來表示。 -----維基百科
因此能夠清晰地從結構中看出,CPU更擅長於邏輯控制,串行運算,GPU更擅長於大規模併發計算。舉一個簡單的例子,一個教授,帶着20個學生完成一個項目,如今項目須要處理1000次100之內的加減乘除運算,這項工做其實並不須要任何邏輯處理,只是大量的工做量的堆疊,這時,就不須要教授親力親爲,教授能夠將任務分配給這20個學生分工完成這項工做,其中教授的角色就至關於CPU,20個學生整體至關於GPU,每一個學生就等於一個ALU。
GPU的處理圖像的優點:
<li>繪製位圖到屏幕上
<li>一遍又一遍地繪製相同的位圖
<li>將同一位圖繪製到不一樣位置,執行旋轉以及縮放處理
<li>具備多核簡單計算能力,能夠處理大量計算數據
GPU的慢在於:
<li>將位圖加載到它的顯存中
JS動畫
缺點:JavaScript在瀏覽器的主線程中運行,而其中還有不少其餘須要運行的JavaScript、樣式計算、佈局、繪製等對其干擾。這也就致使了線程可能出現阻塞,從而形成丟幀的狀況。
優勢:JavaScript的動畫與CSS預先定義好的動畫不一樣,能夠在其動畫過程當中對其進行控制:開始、暫停、回放、停止、取消都是能夠作到的。並且一些動畫效果,好比視差滾動效果,只有JavaScript可以完成。
CSS動畫
缺點:缺少強大的控制能力。並且很難以有意義的方式結合到一塊兒,使得動畫變得複雜且易於出問題。
優勢:瀏覽器能夠對動畫進行優化。它必要時能夠建立圖層,而後在主線程以外運行,也就是開啓GPU加速。
通常來講,Chrome中知足如下任意狀況就會建立圖層:
<li> 3D或透視變換(perspective transform)CSS屬性
<li>使用加速視頻解碼的<video>
節點
<li> 擁有3D(WebGL)上下文或加速的2D上下文的 <canvas>
節點
<li>混合插件(如Flash)
<li> 對本身的opacity作CSS動畫或使用一個動畫webkit變換的元素
<li>擁有加速CSS過濾器的元素
<li>元素有一個包含複合層的後代節點(一個元素擁有一個子元素,該子元素在本身的層裏)
<li>元素有一個z-index較低且包含一個複合層的兄弟元素(換句話說就是該元素在複合層上面渲染)
須要注意的是,若是圖層中某個元素須要重繪,那麼整個圖層都須要重繪。好比一個圖層包含不少節點,其中有個gif圖,gif圖的每一幀,都會重回整個圖層的其餘節點,而後生成最終的圖層位圖。因此這須要經過特殊的方式來強制gif圖屬於本身一個圖層(translateZ(0)或者translate3d(0,0,0)),CSS3的動畫也是同樣。
經過web workers 這樣的多線程來實現動畫
好啦!基本概念已經鋪墊完了,感興趣的話,就來看看在業務線中如何小試牛刀。網頁動畫性能日誌(二)
轉載請說明出處!
參考連接:
<li>http://blog.csdn.net/leer168/article/details/25917093