css 重排與重繪和進(jìn)行優(yōu)化

在介紹瀏覽器的重排與重繪之前亮航,先了解一下瀏覽器的工作原理一旦我們了解了瀏覽器是如何工作的,我們就可以更好的去駕馭它届垫。
現(xiàn)代瀏覽器通常擁有兩個(gè)重要的執(zhí)行線程驮肉,這兩個(gè)線程相互配合來渲染出頁面:

主線程

通常情況下,主線程主要負(fù)責(zé)以下工作:運(yùn)行JavaScript四苇、計(jì)算HTML元素的CSS樣式孝凌、布局頁面、把頁面元素繪制成一個(gè)或多個(gè)位圖月腋、把這些位圖移交給排版線程

排版線程

通常情況下蟀架,排版線程主要負(fù)責(zé)以下工作:通過GPU渲染位圖,并顯示在屏幕上榆骚、向主線程請(qǐng)求更新位圖的可見部分或即將可見的部分片拍、判斷出當(dāng)前頁面處于可見的部分、判斷出即將通過頁面滾動(dòng)而可見的部分妓肢、隨著用戶滾動(dòng)頁面來移動(dòng)這些部分(可見部分的和即將可見的部分)

GPU

排版線程通過GPU把位圖繪制到了屏幕上穆碎。
GPU比較擅長于:繪制位圖到屏幕、重復(fù)的繪制同一個(gè)位圖职恳、在不同的位置所禀,以不同的旋轉(zhuǎn)角度,或者不同的縮放大小來繪制同一個(gè)位圖放钦。
GPU相對(duì)慢的地方:將位圖加載到顯存里色徘。

重排與重繪

瀏覽器下載完頁面中的所有組件——HTML標(biāo)記、JavaScript操禀、CSS褂策、圖片之后會(huì)解析生成兩個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu)——DOM樹和渲染樹。
DOM樹表示頁面結(jié)構(gòu),渲染樹表示DOM節(jié)點(diǎn)如何顯示斤寂。DOM樹中的每一個(gè)需要顯示的節(jié)點(diǎn)在渲染樹種至少存在一個(gè)對(duì)應(yīng)的節(jié)點(diǎn)(隱藏的DOM元素 disply值為none 在渲染樹中沒有對(duì)應(yīng)的節(jié)點(diǎn))耿焊。渲染樹中的節(jié)點(diǎn)被稱為“幀”或“盒”,符合CSS模型的定義,理解頁面元素為一個(gè)具有填充遍搞,邊距罗侯,邊框和位置的盒子。一旦 DOM和渲染樹構(gòu)建完成溪猿,瀏覽器就開始顯示(繪制)頁面元素钩杰。
當(dāng)DOM的變化影響了元素的幾何屬性(寬或高),瀏覽器需要重新計(jì)算元素的幾何屬性诊县,同樣其他元素的幾何屬性和位置也會(huì)因此受到影響讲弄。瀏覽器會(huì)使渲染樹中受到影響的部分失效,并重新構(gòu)造渲染樹依痊。這個(gè)過程稱為重排避除。完成重排后,瀏覽器會(huì)重新繪制受影響的部分到屏幕胸嘁,該過程稱為重繪驹饺。
tips:并不是所有的DOM變化都會(huì)影響幾何屬性,比如改變一個(gè)元素的背景色并不會(huì)影響元素的寬和高缴渊,這種情況下只會(huì)發(fā)生重繪赏壹。

引起重排的情況

很顯然,每次重排衔沼,必然會(huì)導(dǎo)致重繪蝌借,那么,重排會(huì)在哪些情況下發(fā)生指蚁?

  1. 添加或者刪除可見的DOM元素
  2. 元素位置改變
  3. 元素尺寸改變
  4. 元素內(nèi)容改變(例如:一個(gè)文本被另一個(gè)不同尺寸的圖片替代)
  5. 頁面渲染初始化(無法避免)
  6. 瀏覽器窗口尺寸改變

這些都是顯而易見的菩佑,或許你已經(jīng)有過這樣的體會(huì),不間斷地改變?yōu)g覽器窗口大小凝化,導(dǎo)致UI反應(yīng)遲鈍(某些低版本IE下甚至直接掛掉)稍坯,現(xiàn)在你可能恍然大悟,沒錯(cuò)搓劫,正是一次次的重排重繪導(dǎo)致的瞧哟!

transition

了解了重排與重繪之后,現(xiàn)在我們看一下瀏覽器的主線程和排版線程是如何協(xié)同工作來完成一個(gè)CSS Transition的枪向。
假設(shè)我們想要將一個(gè)元素的高度值從100px轉(zhuǎn)換到200px勤揩,如下所示:

div {   
    height: 100px;   
    transition: height 1s linear;   
}   

div:hover {   
    height: 200px;   
} 

主線程和排版線程會(huì)根據(jù)下圖所示時(shí)序圖來完成這個(gè)Transition。注意:在橙色方框中的操作是潛在的耗時(shí)操作秘蛔,藍(lán)色方框中的操作是較快的操作陨亡。


這里寫圖片描述

正如你所見傍衡,整個(gè)過程有很多橙色的方框,意味著瀏覽器有相當(dāng)繁重的工作要處理负蠕,也意味著這個(gè)Transition可能會(huì)出現(xiàn)卡頓蛙埂。
在整個(gè)Transition的每一幀中,瀏覽器都要去重新布局遮糖,繪制頁面绣的,并把最新的位圖對(duì)象加載到GPU。我們前邊了解過止吁,把位圖對(duì)象加載到GPU的內(nèi)存中是個(gè)相對(duì)緩慢的操作被辑。
瀏覽器之所以要在每一幀動(dòng)畫上處理如此繁重的工作是因?yàn)檫@個(gè)元素的內(nèi)容一直在變化燎悍。修改一個(gè)元素的高度可能會(huì)引起其子元素也要相應(yīng)的改變大小敬惦,因此瀏覽器必須去重新布局。重新布局后谈山,主線程必須為該元素重新生成位圖對(duì)象俄删。

transition: transform

由此可見,對(duì)高度進(jìn)行的Transition相對(duì)來說性能比較差奏路,那有一些性能比較好的Transition嗎畴椰?
假設(shè)我們想要把一個(gè)元素從一半大小縮放到實(shí)際大小,并假設(shè)我們使用CSS的transform 屬性來對(duì)它進(jìn)行縮放鸽粉,同時(shí)使用CSS的transition屬性來生成縮放的動(dòng)畫效果斜脂,如下所示:

div {   
    transform: scale(0.5);   
    transition: transform 1s linear;   
}   

div:hover {   
    transform: scale(1.0);   
}  
這里寫圖片描述

我們看到只有很少的幾個(gè)橙色的方框,意味著這個(gè)動(dòng)畫效果可能會(huì)很流暢触机!那么帚戳,一個(gè)元素的transform動(dòng)畫效果與其高度的動(dòng)畫效果有什么不同呢?
根據(jù)定義儡首,CSS的transform屬性不會(huì)改變?cè)氐牟季制危膊粫?huì)影響到其周圍的元素。它把元素當(dāng)做一個(gè)整體看待——縮放整個(gè)元素蔬胯、旋轉(zhuǎn)整個(gè)元素或者移動(dòng)整個(gè)元素对供。
這對(duì)瀏覽器來說是一個(gè)好消息!瀏覽器只需在動(dòng)畫開始的時(shí)候生成這個(gè)元素的位圖對(duì)象氛濒,并把它傳遞給GPU产场。在這之后,瀏覽器無需再做任何重新布局舞竿,繪制頁面以及傳遞位圖對(duì)象的操作了涝动,相反,瀏覽器可以利用GPU擅長的繪制的特點(diǎn)來快速的在不同的位置炬灭,旋轉(zhuǎn)或縮放同一個(gè)位圖對(duì)象醋粟。

設(shè)計(jì)決策

那么靡菇,是否這就意味這我們不要去緩動(dòng)一個(gè)元素的高度?非也米愿,一些情況下厦凤,這是你的設(shè)計(jì)效果的一部分,并且動(dòng)畫效果可以非秤叮快的完成较鼓。也許動(dòng)畫的元素是孤立的,不會(huì)引起頁面其他部分進(jìn)行重新布局违柏;也許該元素只是單純的進(jìn)行重繪博烂,瀏覽器可以快速的完成;也許該元素很小漱竖,瀏覽器只需將很小的位圖對(duì)象傳遞給GPU禽篱。
當(dāng)然了,在不影響你設(shè)計(jì)的視覺效果的情況下馍惹,最好去緩動(dòng)一個(gè)性能較好的CSS屬性躺率,如transform,而不是去緩動(dòng)一個(gè)性能較差的CSS屬性万矾,如height悼吱。舉例來說,假設(shè)你的設(shè)計(jì)中有一個(gè)按鈕良狈,當(dāng)點(diǎn)擊它的時(shí)候會(huì)出來一個(gè)菜單后添,試著去緩動(dòng)菜單的transform屬性來顯示它而不是緩動(dòng)它的top或height屬性來達(dá)到類似的效果。
在動(dòng)畫上特別快的CSS屬性包括:

  1. CSS transform
  2. CSS opacity
  3. CSS filter

下面介紹重排重繪造成的性能開銷和如何進(jìn)行優(yōu)化

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>css3-transform轉(zhuǎn)換成maxtrix</title>
    <style>
        .container{
            position: relative;
            min-height: 400px;
        }
        .box{
            position: absolute;
            top: 0;
            left: 0;
            width: 200px;
            height: 200px;
            border-radius: 50%;
            background-color: orange;
        }

        .animation-run{
            animation: move 4s infinite;
        }

        @keyframes move {
            0%{
                top: 0;
                left: 0;
            }
            25%{
                top: 0;
                left: 200px;
            }
            50%{
                top: 200px;
                left: 200px;
            }
            75%{
                top: 200px;
                left: 0;
            }
        }     
    </style>
</head>
<body>
    <div class="container">
        <div class="box animation-run"></div>
    </div>
</body>
</html>
這個(gè)是沒有開啟3d的動(dòng)畫薪丁,下面進(jìn)行錄制20s看看主要消耗的時(shí)間是哪一部分遇西,主要開銷的時(shí)間是剛剛開始rendering的時(shí)間和造成重排的時(shí)間
image.png
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>css3-transform轉(zhuǎn)換成maxtrix</title>
    <style>
        .container{
            position: relative;
            min-height: 400px;
        }
        .box{
            position: absolute;
            top: 0;
            left: 0;
            width: 200px;
            height: 200px;
            border-radius: 50%;
            background-color: orange;
        }

        .animation-run{
            animation: move 4s infinite;
        }

        /* transform不會(huì)發(fā)生重排重繪 */
        @keyframes move {
            0%{
                transform: translate(0,0);
            }
            25%{
                transform: translate(200px,0);
            }
            50%{
                transform: translate(200px,200px);
            }
            75%{
                transform: translate(0,200px);
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="box animation-run"></div>
    </div>
</body>
</html>
這個(gè)例子是使用transform來做動(dòng)畫,開啟了硬件加速窥突,不會(huì)造成重排重繪努溃,很明顯下圖沒有了paint消耗的時(shí)間
image.png
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>css3-transform轉(zhuǎn)換成maxtrix</title>
    <style>
        .container{
            position: relative;
            min-height: 400px;
        }
        .box{
            position: absolute;
            top: 0;
            left: 0;
            width: 200px;
            height: 200px;
            border-radius: 50%;
            background-color: orange;
        }

        .animation-run{
            animation: move 4s infinite;
        }

        /* 用矩陣加快性能速度 */
        @keyframes move {
            0%{
                transform: matrix(0)
            }
            25%{
                transform: matrix(1, 0, 0, 1, 200, 0)
            }
            50%{
                transform: matrix(1, 0, 0, 1, 200, 200);
            }
            75%{
                transform: matrix(1, 0, 0, 1, 0, 200);
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="box animation-run"></div>
    </div>
</body>
</html>
最終優(yōu)化版本,里面采用了transform做動(dòng)畫阻问,開啟了硬件加速梧税,如果把translate轉(zhuǎn)換成矩陣,進(jìn)行了深度的優(yōu)化
image.png

總結(jié)

重排和重繪是DOM編程中耗能的主要原因之一称近,平時(shí)涉及DOM編程時(shí)可以參考以下幾點(diǎn):

  1. 盡量不要在布局信息改變時(shí)做查詢(會(huì)導(dǎo)致渲染隊(duì)列強(qiáng)制刷新)
  2. 同一個(gè)DOM的多個(gè)屬性改變可以寫在一起(減少DOM訪問第队,同時(shí)把強(qiáng)制渲染隊(duì)列刷新的風(fēng)險(xiǎn)降為0)
  3. 如果要批量添加DOM,可以先讓元素脫離文檔流刨秆,操作完后再帶入文檔流凳谦,這樣只會(huì)觸發(fā)一次重排(fragment元素的應(yīng)用)
  4. 將需要多次重排的元素,position屬性設(shè)為absolute或fixed衡未,這樣此元素就脫離了文檔流尸执,它的變化不會(huì)影響到其他元素家凯。例如有動(dòng)畫效果的元素就最好設(shè)置為絕對(duì)定位。

參考文獻(xiàn)

  1. http://developer.51cto.com/art/201508/488053.htm
  2. http://www.jb51.net/css/348357.html
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末如失,一起剝皮案震驚了整個(gè)濱河市绊诲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌褪贵,老刑警劉巖掂之,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異脆丁,居然都是意外死亡世舰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門槽卫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跟压,“玉大人,你說我怎么就攤上這事晒夹●陕” “怎么了姊氓?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵丐怯,是天一觀的道長。 經(jīng)常有香客問我翔横,道長读跷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任禾唁,我火速辦了婚禮效览,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘荡短。我一直安慰自己丐枉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布掘托。 她就那樣靜靜地躺著瘦锹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪闪盔。 梳的紋絲不亂的頭發(fā)上弯院,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音泪掀,去河邊找鬼听绳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛异赫,可吹牛的內(nèi)容都是我干的椅挣。 我是一名探鬼主播头岔,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼鼠证!你這毒婦竟也來了切油?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤名惩,失蹤者是張志新(化名)和其女友劉穎澎胡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娩鹉,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡攻谁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了弯予。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戚宦。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锈嫩,靈堂內(nèi)的尸體忽然破棺而出受楼,到底是詐尸還是另有隱情,我是刑警寧澤呼寸,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布艳汽,位于F島的核電站,受9級(jí)特大地震影響对雪,放射性物質(zhì)發(fā)生泄漏河狐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一瑟捣、第九天 我趴在偏房一處隱蔽的房頂上張望馋艺。 院中可真熱鬧,春花似錦迈套、人聲如沸捐祠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽踱蛀。三九已至,卻和暖如春芙扎,著一層夾襖步出監(jiān)牢的瞬間星岗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來泰國打工戒洼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俏橘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓圈浇,卻偏偏與公主長得像寥掐,于是被迫代替她去往敵國和親靴寂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容