瀏覽器工作原理24 [#]分層和合成機制:為什么css動畫比JavaScript高效

引用

《瀏覽器工作原理與實踐》

本文主要講解渲染引擎的分層和合成機制,因為分層和合成機制代表了瀏覽器最為先進的合成技術,Chrome 團隊為了做到這一點,做了大量的優化工作。了解其工作原理,有助于拓寬你的視野,而且也有助于你更加深刻地理解 CSS 動畫和 JavaScript 底層工作機制。

一、顯示器是怎么顯示圖像的

每個顯示器都有固定的刷新頻率,通常是 60HZ,也就是每秒更新 60 張圖片,更新的圖片都來自于顯卡中一個叫前緩沖區的地方,顯示器所做的任務很簡單,就是每秒固定讀取 60 次前緩沖區中的圖像,并將讀取的圖像顯示到顯示器上。

那么這里顯卡做什么呢?

顯卡的職責就是合成新的圖像,并將圖像保存到后緩沖區中,一旦顯卡把合成的圖像寫到后緩沖區,系統就會讓后緩沖區和前緩沖區互換,這樣就能保證顯示器能讀取到最新顯卡合成的圖像。通常情況下,顯卡的更新頻率和顯示器的刷新頻率是一致的。但有時候,在一些復雜的場景中,顯卡處理一張圖片的速度會變慢,這樣就會造成視覺上的卡頓。

二、幀 VS 幀率

了解了顯示器是怎么顯示圖像的之后,下面再來明確下幀和幀率的概念,因為這是后續一切分析的基礎。

當你通過滾動條滾動頁面,或者通過手勢縮放頁面時,屏幕上就會產生動畫的效果。之所以你能感覺到有動畫的效果,是因為在滾動或者縮放操作時,渲染引擎會通過渲染流水線生成新的圖片,并發送到顯卡的后緩沖區。

大多數設備屏幕的更新頻率是 60 次 / 秒,這也就意味著正常情況下要實現流暢的動畫效果,渲染引擎需要每秒更新 60 張圖片到顯卡的后緩沖區。

把渲染流水線生成的每一副圖片稱為一幀把渲染流水線每秒更新了多少幀稱為幀率,比如滾動過程中 1 秒更新了 60 幀,那么幀率就是 60Hz(或者 60FPS)。

由于用戶很容易觀察到那些丟失的幀,如果在一次動畫過程中,渲染引擎生成某些幀的時間過久,那么用戶就會感受到卡頓,這會給用戶造成非常不好的印象。

要解決卡頓問題,就要解決每幀生成時間過久的問題,為此 Chrome 對瀏覽器渲染方式做了大量的工作,其中最卓有成效的策略就是引入了分層和合成機制。分層和合成機制代表了當今最先進的渲染技術,所以接下來就來分析下什么是合成和渲染技術

三、如何生成一幀圖像

不過在開始之前,還需要聊一聊渲染引擎是如何生成一幀圖像的。這需要回顧下前面《06 | 渲染流程(下):HTML、CSS 和 JavaScript 文件,是如何變成頁面的?》介紹的渲染流水線。關于其中任意一幀的生成方式,有重排重繪合成三種方式

這三種方式的渲染路徑是不同的,通常渲染路徑越長,生成圖像花費的時間就越多。比如重排,它需要重新根據 CSSOM 和 DOM 來計算布局樹,這樣生成一幅圖片時,會讓整個渲染流水線的每個階段都執行一遍,如果布局復雜的話,就很難保證渲染的效率了。而重繪因為沒有了重新布局的階段,操作效率稍微高點,但是依然需要重新計算繪制信息,并觸發繪制操作之后的一系列操作。

相較于重排和重繪,合成操作的路徑就顯得非常短了,并不需要觸發布局和繪制兩個階段,如果采用了 GPU,那么合成的效率會非常高。

所以,關于渲染引擎生成一幀圖像的幾種方式,按照效率推薦合成方式優先,若實在不能滿足需求,那么就再退后一步使用重繪或者重排的方式。

本文的焦點在合成上,所以接下來就來深入分析下 Chrome 瀏覽器是怎么實現合成操作的。Chrome 中的合成技術,可以用三個詞來概括總結:分層分塊合成

四、分層和合成

通常頁面的組成是非常復雜的,有的頁面里要實現一些復雜的動畫效果,比如點擊菜單時彈出菜單的動畫特效,滾動鼠標滾輪時頁面滾動的動畫效果,當然還有一些炫酷的 3D 動畫特效。如果沒有采用分層機制,從布局樹直接生成目標圖片的話,那么每次頁面有很小的變化時,都會觸發重排或者重繪機制,這種“牽一發而動全身”的繪制策略會嚴重影響頁面的渲染效率。

為了提升每幀的渲染效率,Chrome 引入了分層和合成的機制。那該怎么來理解分層和合成機制呢?

你可以把一張網頁想象成是由很多個圖片疊加在一起的,每個圖片就對應一個圖層,Chrome 合成器最終將這些圖層合成了用于顯示頁面的圖片。如果你熟悉 PhotoShop 的話,就能很好地理解這個過程了,PhotoShop 中一個項目是由很多圖層構成的,每個圖層都可以是一張單獨圖片,可以設置透明度、邊框陰影,可以旋轉或者設置圖層的上下位置,將這些圖層疊加在一起后,就能呈現出最終的圖片了。

在這個過程中,將素材分解為多個圖層的操作就稱為分層,最后將這些圖層合并到一起的操作就稱為合成。所以,分層和合成通常是一起使用的。

考慮到一個頁面被劃分為兩個層,當進行到下一幀的渲染時,上面的一幀可能需要實現某些變換,如平移、旋轉、縮放、陰影或者 Alpha 漸變,這時候合成器只需要將兩個層進行相應的變化操作就可以了,顯卡處理這些操作駕輕就熟,所以這個合成過程時間非常短。

理解了為什么要引入合成和分層機制,下面再來看看 Chrome 是怎么實現分層和合成機制的。

在 Chrome 的渲染流水線中,分層體現在生成布局樹之后,渲染引擎會根據布局樹的特點將其轉換為層樹(Layer Tree),層樹是渲染流水線后續流程的基礎結構。

層樹中的每個節點都對應著一個圖層,下一步的繪制階段就依賴于層樹中的節點。在《06 | 渲染流程(下):HTML、CSS 和 JavaScript 文件,是如何變成頁面的?》中介紹過,繪制階段其實并不是真正地繪出圖片,而是將繪制指令組合成一個列表,比如一個圖層要設置的背景為黑色,并且還要在中間畫一個圓形,那么繪制過程會生成|Paint BackGroundColor:Black | Paint Circle|這樣的繪制指令列表,繪制過程就完成了。

有了繪制列表之后,就需要進入光柵化階段了,光柵化就是按照繪制列表中的指令生成圖片。每一個圖層都對應一張圖片,合成線程有了這些圖片之后,會將這些圖片合成為“一張”圖片,并最終將生成的圖片發送到后緩沖區。這就是一個大致的分層、合成流程。

需要重點關注的是,合成操作是在合成線程上完成的,這也就意味著在執行合成操作時,是不會影響到主線程執行的。這就是為什么經常主線程卡住了,但是 CSS 動畫依然能執行的原因

五、分塊

如果說分層是從宏觀上提升了渲染效率,那么分塊則是從微觀層面提升了渲染效率

通常情況下,頁面的內容都要比屏幕大得多,顯示一個頁面時,如果等待所有的圖層都生成完畢,再進行合成的話,會產生一些不必要的開銷,也會讓合成圖片的時間變得更久。

因此,合成線程會將每個圖層分割為大小固定的圖塊,然后優先繪制靠近視口的圖塊,這樣就可以大大加速頁面的顯示速度。不過有時候, 即使只繪制那些優先級最高的圖塊,也要耗費不少的時間,因為涉及到一個很關鍵的因素——紋理上傳,這是因為從計算機內存上傳到 GPU 內存的操作會比較慢。

為了解決這個問題,Chrome 又采取了一個策略:在首次合成圖塊的時候使用一個低分辨率的圖片。比如可以是正常分辨率的一半,分辨率減少一半,紋理就減少了四分之三。在首次顯示頁面內容的時候,將這個低分辨率的圖片顯示出來,然后合成器繼續繪制正常比例的網頁內容,當正常比例的網頁內容繪制完成后,再替換掉當前顯示的低分辨率內容。這種方式盡管會讓用戶在開始時看到的是低分辨率的內容,但是也比用戶在開始時什么都看不到要好

六、如何利用分層技術優化代碼

通過上面的介紹,相信你已經理解了渲染引擎是怎么將布局樹轉換為漂亮圖片的,理解其中原理之后,你就可以利用分層和合成技術來優化代碼了。

在寫 Web 應用的時候,你可能經常需要對某個元素做幾何形狀變換、透明度變換或者一些縮放操作,如果使用 JavaScript 來寫這些效果,會牽涉到整個渲染流水線,所以 JavaScript 的繪制效率會非常低下。

這時你可以使用 will-change 來告訴渲染引擎你會對該元素做一些特效變換,CSS 代碼如下:

.box {
will-change: transform, opacity;
}

這段代碼就是提前告訴渲染引擎 box 元素將要做幾何變換和透明度變換操作,這時候渲染引擎會將該元素單獨實現一層,等這些變換發生時,渲染引擎會通過合成線程直接去處理變換,這些變換并沒有涉及到主線程,這樣就大大提升了渲染的效率。這也是 CSS 動畫比 JavaScript 動畫高效的原因。

所以,如果涉及到一些可以使用合成線程來處理 CSS 特效或者動畫的情況,就盡量使用 will-change 來提前告訴渲染引擎,讓它為該元素準備獨立的層。但是凡事都有兩面性,每當渲染引擎為一個元素準備一個獨立層的時候,它占用的內存也會大大增加,因為從層樹開始,后續每個階段都會多一個層結構,這些都需要額外的內存,所以你需要恰當地使用 will-change。

七、總結

  • 首先介紹了顯示器顯示圖像的原理,以及幀和幀率的概念,然后基于幀和幀率又介紹渲染引擎是如何實現一幀圖像的。通常渲染引擎生成一幀圖像有三種方式:重排、重繪和合成。其中重排和重繪操作都是在渲染進程的主線程上執行的,比較耗時;而合成操作是在渲染進程的合成線程上執行的,執行速度快,且不占用主線程。
  • 然后重點介紹了瀏覽器是怎么實現合成的,其技術細節主要可以使用三個詞來概括:分層、分塊和合成。
  • 最后還講解了 CSS 動畫比 JavaScript 動畫高效的原因,以及怎么使用 will-change 來優化動畫或特效。

思考時間

觀察下面代碼,結合 Performance 面板、內存面板和分層面板,全面比較在 box 中使用 will-change 和不使用 will-change 的效率、性能和內存占用等情況。

<html>
<head><title> 觀察 will-change</title><style>.box {will-change: transform, opacity;display: block;float: left;width: 40px;height: 40px;margin: 15px;padding: 10px;border: 1px solid rgb(136, 136, 136);background: rgb(187, 177, 37);border-radius: 30px;transition: border-radius 1s ease-out;}body {font-family: Arial;}</style>
</head><body><div id="controls"><button id="start">start</button><button id="stop">stop</button></div><div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div><div class="box"> 旋轉盒子 </div></div><script>let boxes = document.querySelectorAll('.box');  let boxes1 = document.querySelectorAll('.box1');  let start = document.getElementById('start');let stop = document.getElementById('stop');let stop_flag = falsestart.addEventListener('click', function () {stop_flag =  falserequestAnimationFrame(render);})stop.addEventListener('click', function () {stop_flag = true})let  rotate_ = 0let opacity_ = 0function render() { if(stop_flag)return 0 rotate_ = rotate_ + 6if( opacity_ > 1)opacity_ = 0opacity_ = opacity_ + 0.01let command =  'rotate('+rotate_ + 'deg)';for (let index = 0; index < boxes.length; index++) {boxes[index].style.transform  = commandboxes[index].style.opacity = opacity_}requestAnimationFrame(render);}</script>
</body></html>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/88371.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/88371.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/88371.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【數字后端】-什么是RC corner? 每種Corner下有什么區別?

芯片的寄生參數可以在多個corner下提取&#xff0c;他們對應了不同情況的net delay Typical&#xff1a;R和C都是標準值Cmax(Cworst)&#xff1a;C最大的互連角&#xff0c;R小于TypicalCmin(Cbest)&#xff1a;C最小&#xff0c;R大于TypicalRCmax(RCworst)&#xff1a;互連線…

HarmonyOS開發基礎 --鴻蒙倉頡語言基礎語法入門

倉頡編程語言是華為自主研發的一種面向全場景應用開發的現代編程語言&#xff0c;具有高效編程、安全可靠、輕松并發和卓越性能等特點。本節將簡單介紹倉頡語言的部分語法和使用&#xff0c;幫助開發者快速上手。 1.3.1&#xff1a;數據類型 整數 倉頡把整數分為有符號和無符…

Excel文件比較器v1.3,html和js寫的

Excel文件比較器v1.3 版本說明&#xff1a;v1.3 1添加支持文件格式&#xff1a;CSV。 2&#xff0c;添加60條歷史記錄保存功能 - 用于保存比對結果。歷史記錄保存在瀏覽器的localStorage中&#xff0c;這是一個瀏覽器提供的本地存儲機制&#xff0c;數據會一直保留直到用戶…

Kimi“新PPT助手” ,Kimi全新自研的免費AI生成PPT助手

大家好&#xff0c;這里是K姐。 一個幫你用AI輕松生成精美PPT的女子。 前段時間給大家分享了一期用智能體做PPT的對比測評&#xff0c;很多友友都表示&#xff1a;那 Kimi 呢&#xff1f; 今天偶然發現 Kimi 新增了一個叫“新PPT助手”的功能&#xff0c;立馬上手體驗了一下…

MySQL DATETIME類型存儲空間詳解:從8字節到5字節的演變

在MySQL數據庫設計中&#xff0c;DATETIME類型用于存儲日期和時間信息&#xff0c;但其存儲空間大小并非固定不變&#xff0c;而是隨MySQL版本迭代和精度定義動態變化。本文將詳細說明其存儲規則&#xff0c;并提供清晰的對比表格。 一、核心結論 MySQL 5.6.4 是分水嶺&#…

Gartner發布中國企業應用生成式AI指南:避免12 個 GenAI 陷阱

GenAI 技術&#xff08;例如 AI 代理和 DeepSeek&#xff09;的快速迭代導致企業抱有不切實際的期望。本研究借鑒了我們與中國 AI 領導者就常見的 GenAI 陷阱進行的討論&#xff0c;并提供了最終有助于成功采用的建議。 主要發現 接受調查的首席信息官表示&#xff0c;生成式人…

Vue3中ref和reactive的區別與使用場景詳解

在 Vue 3 中&#xff0c;響應式系統進行了全新設計&#xff0c;ref 和 reactive 是其中的核心概念。 ### 一、ref 的使用 ref 適用于基本數據類型&#xff0c;也可以用于對象&#xff0c;但返回的是一個帶 .value 的包裝對象。 js import { ref } from vue const count ref(…

React性能優化:父組件如何導致子組件重新渲染及避免策略

目錄 React性能優化&#xff1a;父組件如何導致子組件重新渲染及避免策略什么是重新渲染&#xff1f;父組件如何"無辜"地讓子組件重新渲染&#xff1f;示例 1: 基礎父組件狀態變更示例 2: 傳遞未變化的原始類型Prop示例 3: 傳遞引用類型Prop&#xff08;對象&#xf…

圖的拓撲排序管理 Go 服務啟動時的組件初始化順序

在構建復雜的 Go 應用程序時&#xff0c;服務的啟動過程往往涉及多個組件的初始化&#xff0c;例如日志、配置、數據庫連接、緩存、服務管理器、適配器等等。這些組件之間通常存在著復雜的依賴關系&#xff1a;日志可能需要配置信息&#xff0c;數據庫連接可能依賴日志和追蹤&a…

【物理重建】SPLART:基于3D高斯潑濺的鉸鏈估計與部件級重建

標題&#xff1a;《SPLART: Articulation Estimation and Part-Level Reconstruction with 3D Gaussian Splatting》 項目&#xff1a;https://github.com/ripl/splart 文章目錄 摘要一、引言二、相關工作2.1 數據驅動的鉸鏈學習2.2 物體重建的表征方法2.3 鉸鏈物體重建 三、方…

vscode中vue自定義組件的標簽失去特殊顏色高亮

遇到的問題 最近接觸了一個歷史遺留項目時&#xff0c;我遭遇了堪稱"史詩級屎山"的代碼結構——各種命名混亂的自定義組件和原生HTML標簽混雜在一起&#xff0c;視覺上完全無法區分。這讓我突然想起&#xff0c;之前在使用vue或者其他框架開發的時候&#xff0c;只要…

【Dify精講】第19章:開源貢獻指南

今天&#xff0c;讓我們深入 Dify 的開源貢獻體系&#xff0c;看看這個項目是如何在短短時間內聚集起一個活躍的開發者社區的。作為想要參與 Dify 開發的你&#xff0c;這一章將是你的實戰指南。 一、代碼貢獻流程&#xff1a;從想法到合并的完整路徑 1.1 貢獻前的準備工作 …

Web攻防-CSRF跨站請求偽造Referer同源Token校驗復用刪除置空聯動上傳或XSS

知識點&#xff1a; 1、Web攻防-CSRF-原理&檢測&利用&防御 2、Web攻防-CSRF-防御-Referer策略隱患 3、Web攻防-CSRF-防御-Token校驗策略隱患 一、演示案例-WEB攻防-CSRF利用-原理&構造 CSRF 測試功能點 刪除帳戶 更改電子郵件 如果不需要舊密碼&#xff0c;請…

Drag-and-Drop LLMs: Zero-Shot Prompt-to-Weights

“拖拽式大模型定制”&#xff08;Drag-and-Drop LLMs: Zero-Shot Prompt-to-Weights&#xff09;。 核心問題&#xff1a; 現在的大模型&#xff08;比如GPT-4&#xff09;很厲害&#xff0c;但想讓它們專門干好某個特定任務&#xff08;比如解數學題、寫代碼&#xff09;&am…

抖音視頻怎么去掉抖音號水印保存

隨著抖音成為短視頻平臺的領軍者&#xff0c;越來越多的人喜歡在上面拍攝、觀看和分享各種創意內容。對于用戶來說&#xff0c;下載抖音視頻并去除水印保存&#xff0c;以便后續使用或分享成為了一種常見需求。抖音號水印的存在雖然能幫助平臺追溯視頻源頭&#xff0c;但也讓許…

【RAG技術(1)】大模型為什么需要RAG

文章目錄 為什么需要RAG&#xff1f;RAG的工作原理關鍵的Embedding技術 RAG vs 模型微調&#xff1a;選擇的核心邏輯RAG的關鍵挑戰與解決思路1. 檢索質量決定一切2. 上下文長度限制 實際應用場景分析企業知識問答技術文檔助手法律咨詢系統 構建RAG系統的關鍵步驟總結 為什么需要…

JS紅寶書筆記 - 8.1 理解對象

對象就是一組沒有特定順序的值&#xff0c;對象的每個屬性或者方法都可由一個名稱來標識&#xff0c;這個名稱映射到一個值。可以把對象想象成一張散列表&#xff0c;其中的內容就是一組名值對&#xff0c;值可以是數據或者函數 創建自定義對象的通常方式是創建Object的一個新…

Meson介紹及編譯Glib庫

一.概述 1.Meson 的簡介 Meson&#xff08;The Meson Build System&#xff09;是個項目構建系統&#xff0c;類似的構建系統有 Makefile、CMake、automake …。 Meson 是一個由 Python 實現的開源項目&#xff0c;其思想是&#xff0c;開發人員花費在構建調試上的每一秒都是…

Qt元對象系統實踐指南:從入門到應用

目錄 摘要 元對象系統核心概念 項目示例&#xff1a;動態UI配置工具 元對象系統在項目中的應用 1. 信號與槽機制 2. 動態屬性系統 3. 運行時反射能力 4. 屬性綁定與響應 實際項目應用場景 動態UI配置 對象序列化 插件系統 性能優化建議 結論 參考資料 摘要 本文…

Kafka 與其他 MQ 的對比分析:RabbitMQ/RocketMQ 選型指南(一)

消息隊列簡介 在當今的分布式系統架構中&#xff0c;消息隊列&#xff08;Message Queue&#xff0c;MQ&#xff09;扮演著舉足輕重的角色。隨著業務規模的不斷擴大和系統復雜度的日益提升&#xff0c;各個組件之間的通信和協同變得愈發關鍵 。消息隊列作為一種異步的通信機制…