承接上一篇:【CSS3進階】酷炫的3D旋轉透視 。javascript
最近入坑 Web 動畫,因此把本身的學習過程記錄一下分享給你們。php
CSS3 3D 行星運轉 demo 頁面請戳:Demo。(建議使用Chrome打開)css
本文完整的代碼,以及更多的 CSS3 效果,在我 Github 上能夠看到,也但願你們能夠點個 star。html
嗯,可能有些人打不開 demo 或者頁面亂了,貼幾張效果圖:(圖片有點大,耐心等待一會)html5
隨機再截屏了一張:java
強烈建議你點進 Demo 頁感覺一下 CSS3 3D 的魅力,圖片能展示的東西畢竟有限。css3
而後,這個 CSS3 3D 行星運轉動畫的製做過程再也不詳細贅述,本篇的重點放在 Web 動畫介紹及性能優化方面。詳細的 CSS3 3D 能夠回看上一篇博客:【CSS3進階】酷炫的3D旋轉透視。簡單的思路:git
1. 利用上一篇所製做的 3D 照片牆爲原型,改造而來;github
2. 每個球體的製做,想了許多方法,最終使用了這種折中的方式,每個球體自己也是一個 CSS3 3D 圖形。而後在製做過程當中使用 Sass 編寫 CSS 能夠減小不少繁瑣的編寫 CSS 動畫的過程;web
3. Demo 當中有使用 Javascript 寫了一個鼠標跟隨的監聽事件,去掉這個事件,整個行星運動動畫自己是純 CSS 實現的。
下面將進入本文的重點,從性能優化的角度講講瀏覽器渲染展現原理,瀏覽器的重繪與重排,動畫的性能檢測優化等:
小標題起得有點大,咱們知道,不一樣瀏覽器的內核(渲染引擎,Rendering Engine)是不同的,例如如今最主流的 chrome 瀏覽器的內核是 Blink 內核(在Chrome(28及日後版本)、Opera(15及日後版本)和Yandex瀏覽器中使用),火狐是 Gecko,IE 是 Trident ,瀏覽器內核負責對網頁語法的解釋並渲染(顯示)網頁,不一樣瀏覽器內核的工做原理並不徹底一致。
因此其實下面將主要討論的是 chrome 瀏覽器下的渲染原理。由於 chrome 內核渲染可查證的資料較多,對於其餘內核的瀏覽器不敢妄下定論,因此下面展開的討論默認是針對 chrome 瀏覽器的。
首先,我要拋出一點結論:
這裏談到了 GPU 加速,爲何 GPU 可以加速 3D 變換?這一切又必需要從瀏覽器底層的渲染講起,瀏覽器渲染展現網頁的過程,老生常談,面試必問,大體分爲:
1. 解析HTML(HTML Parser)
2. 構建DOM樹(DOM Tree)
3. 渲染樹構建(Render Tree)
4. 繪製渲染樹(Painting)
簡單解釋一下,經過請求獲得的 HTML 通過解析(HTML parser)生成 DOM Tree。而在 CSS 解析完畢後,須要將解析的結果與 DOM Tree 的內容一塊兒進行分析創建一棵 Render Tree,最終用來進行繪圖(Painting)。
找到了一張很經典的圖:
這個渲染過程做爲一個基礎知識,繼續往下深刻。
當頁面加載並解析完畢後,它在瀏覽器內表明了一個你們十分熟悉的結構:DOM(Document Object Model,文檔對象模型)。在瀏覽器渲染一個頁面時,它使用了許多沒有暴露給開發者的中間表現形式,其中最重要的結構即是層(layer)。
這個層就是本文重點要討論的內容:
而在 Chrome 中,存在有不一樣類型的層: RenderLayer(負責 DOM 子樹),GraphicsLayer(負責 RenderLayer 的子樹)。接下來咱們所討論的將是 GraphicsLayer 層。
GraphicsLayer 層是做爲紋理(texture)上傳給 GPU 的。
這裏這個紋理很重要,那麼,
這裏的紋理指的是 GPU 的一個術語:能夠把它想象成一個從主存儲器(例如 RAM)移動到圖像存儲器(例如 GPU 中的 VRAM)的位圖圖像(bitmap image)。一旦它被移動到 GPU 中,你能夠將它匹配成一個網格幾何體(mesh geometry),在 Chrome 中使用紋理來從 GPU 上得到大塊的頁面內容。經過將紋理應用到一個很是簡單的矩形網格就能很容易匹配不一樣的位置(position)和變形(transformation),這也就是 3D CSS 的工做原理。
提及來很難懂,直接看例子,在 chrome 中,咱們是能夠看到上文所述的 GraphicsLayer -- 層的概念。在開發者工具中,咱們進行以下選擇調出 show layer borders 選項:
在一個極簡單的頁面,咱們能夠看到以下所示,這個頁面只有一個層。藍色網格表示瓦片(tile),你能夠把它們看成是層的單元(並非層),Chrome 能夠將它們做爲一個大層的部分上傳給 GPU:
由於上面的頁面十分簡單,因此並無產生層,可是在很複雜的頁面中,譬如咱們給元素設置一個 3D CSS 屬性來變換它,咱們就能看到當元素擁有本身的層時是什麼樣子。
注意橘黃色的邊框,它畫出了該視圖中層的輪廓:
上面示意圖中黃色邊框框住的層,就是 GraphicsLayer ,它對於咱們的 Web 動畫而言很是重要,一般,Chrome 會將一個層的內容在做爲紋理上傳到 GPU 前先繪製(paint)進一個位圖中。若是內容不會改變,那麼就沒有必要重繪(repaint)層。
這樣作的意義在於:花在重繪上的時間能夠用來作別的事情,例如運行 JavaScript,若是繪製的時間很長,還會形成動畫的故障與延遲。
那麼一個元素何時會觸發建立一個層?從目前來講,知足如下任意狀況便會建立層:
對於靜態 Web 頁面而言,層在第一次被繪製出來以後將不會被改變,但對於 Web 動畫,頁面的 DOM 元素是在不斷變換的,若是層的內容在變換過程當中發生了改變,那麼層將會被重繪(repaint)。
強大的 chrome 開發者工具提供了工具讓咱們能夠查看到動畫頁面運行中,哪些內容被從新繪製了:
在舊版的 chrome 中,是有 show paint rects 這一個選項的,能夠查看頁面有哪些層被重繪了,並以紅色邊框標識出來。
可是新版的 chrome 貌似把這個選項移除了,如今的選項是 enable paint flashing ,其做用也是標識出網站動態變換的地方,而且以綠色邊框標識出來。
看上面的示意圖,能夠看到頁面中有幾處綠色的框,表示發生了重繪。注意 Chrome 並不會始終重繪整個層,它會嘗試智能的去重繪 DOM 中失效的部分。
按照道理,頁面發生這麼多動畫,重繪應該很頻繁纔對,可是上圖個人行星動畫中我只看到了寥寥綠色重繪框,個人我的理解是,一是 GPU 優化,二是若是整個動畫頁面只有一個層,那麼運用了 transform 進行變換,頁面必然須要重繪,可是採用分層(GraphicsLayer )技術,也就是上面說符合狀況的元素各自建立層,那麼一個元素所建立的層運用 transform 變換,譬如 rotate 旋轉,這個時候該層的旋轉變換並無影響到其餘層,那麼該層不必定須要被重繪。(我的之見,還請提出指正)。
瞭解層的重繪對 Web 動畫的性能優化相當重要。
是什麼緣由致使失效(invalidation)進而強制重繪的呢?這個問題很難詳盡回答,由於存在大量致使邊界失效的狀況。最多見的狀況就是經過操做 CSS 樣式來修改 DOM 或致使重排。
查找引起重繪和重排根源的最好辦法就是使用開發者工具的時間軸和 enable paint flashing 工具,而後試着找出剛好在重繪/重排前修改了 DOM 的地方。
總結
那麼瀏覽器是如何從 DOM 元素到最終動畫的展現呢?
Web 動畫很大一部分開銷在於層的重繪,以層爲基礎的複合模型對渲染性能有着深遠的影響。當不須要繪製時,複合操做的開銷能夠忽略不計,所以在試着調試渲染性能問題時,首要目標就是要避免層的重繪。那麼這就給動畫的性能優化提供了方向,減小元素的重繪與迴流。
這裏首先要分清兩個概念,重繪與迴流。
當渲染樹(render Tree)中的一部分(或所有)由於元素的規模尺寸,佈局,隱藏等改變而須要從新構建。這就稱爲迴流(reflow),也就是從新佈局(relayout)。
每一個頁面至少須要一次迴流,就是在頁面第一次加載的時候。在迴流的時候,瀏覽器會使渲染樹中受到影響的部分失效,並從新構造這部分渲染樹,完成迴流後,瀏覽器會從新繪製受影響的部分到屏幕中,該過程成爲重繪。
當render tree中的一些元素須要更新屬性,而這些屬性只是影響元素的外觀,風格,而不會影響佈局的,好比 background-color 。則就叫稱爲重繪。
值得注意的是,迴流必將引發重繪,而重繪不必定會引發迴流。
明顯,迴流的代價更大,簡單而言,當操做元素會使元素修改它的大小或位置,那麼就會發生迴流。
因此對於頁面而言,咱們的宗旨就是儘可能減小頁面的迴流重繪,簡單的一個栗子:
// 下面這種方式將會致使迴流reflow兩次 var newWidth = aDiv.offsetWidth + 10; // Read aDiv.style.width = newWidth + 'px'; // Write var newHeight = aDiv.offsetHeight + 10; // Read aDiv.style.height = newHeight + 'px'; // Write // 下面這種方式更好,只會迴流reflow一次 var newWidth = aDiv.offsetWidth + 10; // Read var newHeight = aDiv.offsetHeight + 10; // Read aDiv.style.width = newWidth + 'px'; // Write aDiv.style.height = newHeight + 'px'; // Write
上面四句,由於涉及了 offsetHeight 操做,瀏覽器強制 reflow 了兩次,而下面四句合併了 offset 操做,因此減小了一次頁面的迴流。
減小回流、重繪其實就是須要減小對渲染樹的操做(合併屢次多DOM和樣式的修改),並減小對一些style信息的請求,儘可能利用好瀏覽器的優化策略。
其實瀏覽器自身是有優化策略的,若是每句 Javascript 都去操做 DOM 使之進行迴流重繪的話,瀏覽器可能就會受不了。因此不少瀏覽器都會優化這些操做,瀏覽器會維護 1 個隊列,把全部會引發迴流、重繪的操做放入這個隊列,等隊列中的操做到了必定的數量或者到了必定的時間間隔,瀏覽器就會 flush 隊列,進行一個批處理。這樣就會讓屢次的迴流、重繪變成一次迴流重繪。
可是也有例外,由於有的時候咱們須要精確獲取某些樣式信息,下面這些:
這個時候,瀏覽器爲了反饋最精確的信息,須要當即迴流重繪一次,確保給到咱們的信息是準確的,因此可能致使 flush 隊列提早執行了。
二者均可以在頁面上隱藏節點。不一樣之處在於,
從性能的角度而言,便是迴流與重繪的方面,
他們二者在優化中 visibility:hidden 會顯得更好,由於咱們不會由於它而去改變了文檔中已經定義好的顯示層次結構了。
對子元素的影響:
比錯誤放置的動畫更糟的事情是致使頁面卡頓的動畫。 這將讓用戶以爲失望和不悅,而且可能但願您根本沒必要費心去設置動畫!
不一樣樣式在消耗性能方面是不一樣的,改變一些屬性的開銷比改變其餘屬性要多,所以更可能使動畫卡頓。
例如,與改變元素的文本顏色相比,改變元素的 box-shadow
將須要開銷大不少的繪圖操做。 改變元素的 width
可能比改變其 transform
要多一些開銷。如 box-shadow 屬性,從渲染角度來說十分耗性能,緣由就是與其餘樣式相比,它們的繪製代碼執行時間過長。這就是說,若是一個耗性能嚴重的樣式常常須要重繪,那麼你就會遇到性能問題。其次你要知道,沒有不變的事情,在今天性能不好的樣式,可能明天就被優化,而且瀏覽器之間也存在差別。
所以關鍵在於,你要藉助開發工具來分辨出性能瓶頸所在,而後設法減小瀏覽器的工做量。
好在 chrome 瀏覽器提供了許多強大的功能,讓咱們能夠檢測咱們的動畫性能,除了上面提到的,咱們還能夠經過勾選下面這個 show FPS meter 顯示頁面的 FPS 信息,以及 GPU 的使用率:
官方文檔說,這是一個仍處於實驗階段的功能,因此在將來版本的瀏覽器中該功能的語法和行爲可能隨之改變。
使用方法示例:(具體每一個取值的意義,去翻翻文檔)
will-change: auto will-change: scroll-position will-change: contents will-change: transform // Example of <custom-ident> will-change: opacity // Example of <custom-ident> will-change: left, top // Example of two <animateable-feature> will-change: unset will-change: initial will-change: inherit // 示例 .example{ will-change: transform; }
will-change 爲 web 開發者提供了一種告知瀏覽器該元素會有哪些變化的方法,這樣瀏覽器能夠在元素屬性真正發生變化以前提早作好對應的優化準備工做。 這種優化能夠將一部分複雜的計算工做提早準備好,使頁面的反應更爲快速靈敏。
值得注意的是,用好這個屬性並非很容易:
GPU 可以加速 Web 動畫,這個上文已經反覆提到了。
3D transform 會啓用GPU加速,例如 translate3D, scaleZ 之類,固然咱們的頁面可能並無 3D 變換,可是不表明咱們不能啓用 GPU 加速,在非 3D 變換的頁面也使用 3D transform 來操做,算是一種 hack 加速法。咱們實際上不須要z軸的變化,可是仍是假模假樣地聲明瞭,去欺騙瀏覽器。
參考文獻:
Rendering: repaint, reflow/relayout, restyle
How (not) to trigger a layout in WebKit
Accelerated Rendering in Chrome
到此本文結束,若是還有什麼疑問或者建議,能夠多多交流,原創文章,文筆有限,才疏學淺,文中如有不正之處,萬望告知。
CSS3 3D 行星運轉 demo 頁面請戳:Demo。(建議使用Chrome打開)
本文完整的代碼,以及更多的 CSS3 效果,在我 Github 上能夠看到,也但願你們能夠點個 star。
若是本文對你有幫助,請點下推薦,寫文章不容易。