我們來看看chrome控制檯動畫的效能
問題1:我們使用left/top來看看動畫的效能
我們的頁面結構如下:body { padding: 30px; text-align: center; } .container { position: relative; min-height: 400px; -webkit-tap-highlight-color: rgba(0,0,0,0); } .ball { position: absolute; top: 0; left: 0; width: 100px; height: 100px; border-radius: 50%; box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.75); } .ball:nth-last-child(1) { background-image: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/splogo.png'); background-position: center center; background-repeat: no-repeat; } .ball-running { -webkit-animation: run-around 4s infinite; animation: run-around 4s infinite; } @keyframes run-around { 0%: { top: 0; left: 0; } 25% { top: 0; left: 200px; } 50% { top: 200px; left: 200px; } 75% { top: 200px; left: 0; } }
<div class="container">
<div class="ball" id="ball"></div>
</div>
然後我們看看JS程式碼:
var balls = document.getElementById('ball');
balls.classList.add("ball");
balls.classList.add('ball-running');
我們從控制檯來看看動畫的執行效能:
首先我們看到動畫執行的時候一直有一個綠色框,這表示重繪的區域,而且是一直重繪的:
第二步:我們看看這個動畫的時間分佈
我們可以看到頁面重繪的時間是107.3ms,二頁面迴流的時間是186.8ms
第三步:我們看看具體的事件步驟
我們可以看到每次都會重新計算樣式,位置大小等,然後更新每一層的元素的相關資訊,然後合成每一層的內容顯示。這將導致很嚴重的重繪和迴流,對於效能的消耗要大得多。注意:這裡只有一個圖層,就像PS圖層一樣,我們只是會重新繪製圖層中的元素,而不是整個圖層本身,因此圖層本身並沒有變成綠色。
問題2:我們看看使用transform實現的動畫效能
修改CSS樣式如下:
第一步:我們看到瀏覽器為該元素產生了一個獨立的層body { padding: 30px; text-align: center; } .container { position: relative; min-height: 400px; -webkit-tap-highlight-color: rgba(0,0,0,0); } .ball { position: absolute; top: 0; left: 0; width: 100px; height: 100px; border-radius: 50%; box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.75); } .ball:nth-last-child(1) { background-image: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/splogo.png'); background-position: center center; background-repeat: no-repeat; } .ball-running { animation: run-around 4s infinite; } @keyframes run-around { 0% { transform: translate(0, 0); } 25% { transform: translate(200px, 0); } 50% { transform: translate(200px, 200px); } 75% { transform: translate(0, 200px); } }
第二步:我們看看總時間分佈
可以看到重繪的時間明顯減少
第三步:看看事件的順序
我們看到這裡主要牽涉到樣式計算,然後更新特定層的元素,最後再合成就可以了,明顯沒有了前面的重繪和迴流操作。注意:因為這裡是transform屬性的改變,如果是改變left/top值,那個這個圖層還是會發生迴流和重繪操作的。
瀏覽器接收到頁面文件後,會將文件中的標記語言解析為DOM樹。DOM樹和CSS結合後形成瀏覽器構建頁面的渲染樹。渲染樹中包含了大量的渲染元素,每一個渲染元素會被分到一個圖層中,每個圖層又會被載入到GPU形成渲染紋理,而圖層在GPU中 transform 是不會觸發 repaint 的(只是重新計算元素的樣式,然後根據計算的樣式更新特定層的元素,然後把更新後的層和其他層合成起來,這個過程中是沒有repaint的),這一點非常類似3D繪圖功能,最終這些使用 transform 的圖層都會由獨立的合成器程序進行處理。
注意:因為上面我們使用的是2D變化,而不是3D變換。3D 和 2D transform 的區別就在於,瀏覽器在頁面渲染前為3D動畫建立獨立的複合圖層,而在執行期間為2D動畫建立。動畫開始時,生成新的複合圖層並載入為GPU的紋理用於初始化 repaint。然後由GPU的複合器操縱整個動畫的執行。最後當動畫結束時,再次執行 repaint 操作刪除複合圖層。所以,上面的例子在開始和結束的時候會有兩次repaint操作。
問題2:不要弄混淆,即使單獨建立了一個圖層,如果變動了重繪和重排的指標,也是會迴流和重排的
例子的程式碼如下:
.box{
width:100px;
height:100px;
background-color: #ccc;
position:absolute;
top:0;
left:0;
transition:left 5s linear;
transform: translateZ(0);
/*這時候雖然給我們的box元素放在一個獨立的層當中,但是我們依然修改了他的left屬性,所以他肯定會重繪迴流,除非我們修改為transform屬性和opacity*/
}
.hv{
left:400px;
}
JS程式碼如下:
window.onload=function(){
var box=document.querySelector('.box');
box.classList.add('hv');
}
DOM結構如下:
<div class="box"></div>
這時候我們通過了transform:translateZ為這個元素建立了一個獨立的圖層,但是我們的transition依然是通過left來完成的,這時候也是會發生迴流,重繪,合成操作的。但是這時候的迴流,重繪,合成操作都是在該圖層中進行的,而不會影響其他的圖層。
我們把上面的動畫使用transform來完成
.box1{
width:100px;
height:100px;
background-color: #ccc;
position:absolute;
top:0;
left:0;
transition:transform 5s;
-webkit-transition: -webkit-transform 2s;
-webkit-backface-visibility:hidden;
transform: translate3d(0,0,0);
-webkit-perspective:1000;
}
.hv1{
transform: translate3d(200px,0,0);
}
修改CSS屬性如上,我們看看結果如何
這時候我們看到已經沒有前面說的重繪和迴流了,這就是transform動畫的優秀之處。使用tranforms來實現移動效果的元素將會被正常繪製,同時不會觸發對其他元素的繪製。這種處理方式和思想跟影象處理軟體(比如Sketch/GIMP/Photoshop)是一致的,它們都是可以在影象中的某個單個圖層上做操作,最後合併所有圖層得到最終的影象。摘抄自簡化繪製的複雜度、減小繪製區域