您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“CSS3 3D行星運(yùn)轉(zhuǎn)與瀏覽器渲染的原理是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
隨機(jī)再截屏了一張:
強(qiáng)烈建議你點(diǎn)進(jìn) Demo - CSS3 3D 行星運(yùn)轉(zhuǎn)[2] 頁(yè)感受一下 CSS3 3D 的魅力,圖片能展現(xiàn)的東西畢竟有限。
然后,這個(gè) CSS3 3D 行星運(yùn)轉(zhuǎn)動(dòng)畫的制作過程不再詳細(xì)贅述,本篇的重點(diǎn)放在 Web 動(dòng)畫介紹及性能優(yōu)化方面。詳細(xì)的 CSS3 3D 可以回看上一篇博客:【CSS3進(jìn)階】酷炫的3D旋轉(zhuǎn)透視[3]。簡(jiǎn)單的思路:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
利用上一篇所制作的 3D 照片墻為原型,改造而來;
每一個(gè)球體的制作,想了許多方法,最終使用了這種折中的方式,每一個(gè)球體本身也是一個(gè) CSS3 3D 圖形。然后在制作過程中使用 Sass 編寫 CSS 可以減少很多繁瑣的編寫 CSS 動(dòng)畫的過程;
Demo 當(dāng)中有使用 Javascript 寫了一個(gè)鼠標(biāo)跟隨的監(jiān)聽事件,去掉這個(gè)事件,整個(gè)行星運(yùn)動(dòng)動(dòng)畫本身是純 CSS 實(shí)現(xiàn)的。
下面將進(jìn)入本文的重點(diǎn),從性能優(yōu)化的角度講講瀏覽器渲染展示原理,瀏覽器的重繪與重排,動(dòng)畫的性能檢測(cè)優(yōu)化等:
小標(biāo)題起得有點(diǎn)大,我們知道,不同瀏覽器的內(nèi)核(渲染引擎,Rendering Engine)是不一樣的,例如現(xiàn)在最主流的 chrome 瀏覽器的內(nèi)核是 Blink 內(nèi)核(在Chrome(28及往后版本)、Opera(15及往后版本)和Yandex瀏覽器中使用),火狐是 Gecko,IE 是 Trident 。
瀏覽器內(nèi)核負(fù)責(zé)對(duì)網(wǎng)頁(yè)語(yǔ)法的解釋并渲染(顯示)網(wǎng)頁(yè),不同瀏覽器內(nèi)核的工作原理并不完全一致。
所以其實(shí)下面將主要討論的是 chrome 瀏覽器下的渲染原理。因?yàn)?chrome 內(nèi)核渲染可查證的資料較多,對(duì)于其他內(nèi)核的瀏覽器不敢妄下定論,所以下面展開的討論默認(rèn)是針對(duì) chrome 瀏覽器的。
首先,我要拋出一點(diǎn)結(jié)論:
這里談到了 GPU 加速,為什么 GPU 能夠加速 3D 變換?這一切又必須要從瀏覽器底層的渲染講起,瀏覽器渲染展示網(wǎng)頁(yè)的過程,老生常談,面試必問,大致分為:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
解析HTML(HTML Parser)
構(gòu)建DOM樹(DOM Tree)
渲染樹構(gòu)建(Render Tree)
繪制渲染樹(Painting)
找到了一張很經(jīng)典的圖:
瀏覽器渲染頁(yè)面過程
這個(gè)渲染過程作為一個(gè)基礎(chǔ)知識(shí),繼續(xù)往下深入。
當(dāng)頁(yè)面加載并解析完畢后,它在瀏覽器內(nèi)代表了一個(gè)大家十分熟悉的結(jié)構(gòu):DOM(Document Object Model,文檔對(duì)象模型)。在瀏覽器渲染一個(gè)頁(yè)面時(shí),它使用了許多沒有暴露給開發(fā)者的中間表現(xiàn)形式,其中最重要的結(jié)構(gòu)便是層(layer)。
這個(gè)層就是本文重點(diǎn)要討論的內(nèi)容:
而在 Chrome 中,存在有不同類型的層:RenderLayer(負(fù)責(zé) DOM 子樹),GraphicsLayer(負(fù)責(zé) RenderLayer 的子樹)。接下來我們所討論的將是 GraphicsLayer 層。
GraphicsLayer 層是作為紋理(texture)上傳給 GPU 的。
這里這個(gè)紋理很重要,那么,
這里的紋理指的是 GPU 的一個(gè)術(shù)語(yǔ):可以把它想象成一個(gè)從主存儲(chǔ)器(例如 RAM)移動(dòng)到圖像存儲(chǔ)器(例如 GPU 中的 VRAM)的位圖圖像(bitmap image)。一旦它被移動(dòng)到 GPU 中,你可以將它匹配成一個(gè)網(wǎng)格幾何體(mesh geometry),在 Chrome 中使用紋理來從 GPU 上獲得大塊的頁(yè)面內(nèi)容。
通過將紋理應(yīng)用到一個(gè)非常簡(jiǎn)單的矩形網(wǎng)格就能很容易匹配不同的位置(position)和變形(transformation),這也就是 3D CSS 的工作原理。
說起來很難懂,直接看例子,在 chrome 中,我們是可以看到上文所述的 GraphicsLayer -- 層的概念。在開發(fā)者工具中,我們進(jìn)行如下選擇調(diào)出 show layer borders 選項(xiàng):
在一個(gè)極簡(jiǎn)單的頁(yè)面,我們可以看到如下所示,這個(gè)頁(yè)面只有一個(gè)層。藍(lán)色網(wǎng)格表示瓦片(tile),你可以把它們當(dāng)作是層的單元(并不是層),Chrome 可以將它們作為一個(gè)大層的部分上傳給 GPU:
因?yàn)樯厦娴捻?yè)面十分簡(jiǎn)單,所以并沒有產(chǎn)生層,但是在很復(fù)雜的頁(yè)面中,譬如我們給元素設(shè)置一個(gè) 3D CSS 屬性來變換它,我們就能看到當(dāng)元素?fù)碛凶约旱膶訒r(shí)是什么樣子。
注意橘黃色的邊框,它畫出了該視圖中層的輪廓:
上面示意圖中黃色邊框框住的層,就是 GraphicsLayer ,它對(duì)于我們的 Web 動(dòng)畫而言非常重要,通常,Chrome 會(huì)將一個(gè)層的內(nèi)容在作為紋理上傳到 GPU 前先繪制(paint)進(jìn)一個(gè)位圖中。如果內(nèi)容不會(huì)改變,那么就沒有必要重繪(repaint)層。
這樣做的意義在于:花在重繪上的時(shí)間可以用來做別的事情,例如運(yùn)行 JavaScript,如果繪制的時(shí)間很長(zhǎng),還會(huì)造成動(dòng)畫的故障與延遲。
那么一個(gè)元素什么時(shí)候會(huì)觸發(fā)創(chuàng)建一個(gè)層?從目前來說,滿足以下任意情況便會(huì)創(chuàng)建層:
3D 或透視變換(perspective、transform) CSS 屬性
使用加速視頻解碼的
擁有 3D (WebGL) 上下文或加速的 2D 上下文的 元素
混合插件(如 Flash)
對(duì)自己的 opacity 做 CSS 動(dòng)畫或使用一個(gè)動(dòng)畫變換的元素
擁有加速 CSS 過濾器的元素
元素有一個(gè)包含復(fù)合層的后代節(jié)點(diǎn)(換句話說,就是一個(gè)元素?fù)碛幸粋€(gè)子元素,該子元素在自己的層里)
元素有一個(gè) z-index 較低且包含一個(gè)復(fù)合層的兄弟元素(換句話說就是該元素在復(fù)合層上面渲染)
對(duì)于靜態(tài) Web 頁(yè)面而言,層在第一次被繪制出來之后將不會(huì)被改變,但對(duì)于 Web 動(dòng)畫,頁(yè)面的 DOM 元素是在不斷變換的,如果層的內(nèi)容在變換過程中發(fā)生了改變,那么層將會(huì)被重繪(repaint)。
強(qiáng)大的 chrome 開發(fā)者工具提供了工具讓我們可以查看到動(dòng)畫頁(yè)面運(yùn)行中,哪些內(nèi)容被重新繪制了:
在舊版的 chrome 中,是有 show paint rects 這一個(gè)選項(xiàng)的,可以查看頁(yè)面有哪些層被重繪了,并以紅色邊框標(biāo)識(shí)出來。
但是新版的 chrome 貌似把這個(gè)選項(xiàng)移除了,現(xiàn)在的選項(xiàng)是 enable paint flashing ,其作用也是標(biāo)識(shí)出網(wǎng)站動(dòng)態(tài)變換的地方,并且以綠色邊框標(biāo)識(shí)出來。
看上面的示意圖,可以看到頁(yè)面中有幾處綠色的框,表示發(fā)生了重繪。注意 Chrome 并不會(huì)始終重繪整個(gè)層,它會(huì)嘗試智能的去重繪 DOM 中失效的部分。
按照道理,頁(yè)面發(fā)生這么多動(dòng)畫,重繪應(yīng)該很頻繁才對(duì),但是上圖我的行星動(dòng)畫中我只看到了寥寥綠色重繪框,我的個(gè)人理解是,一是 GPU 優(yōu)化,二是如果整個(gè)動(dòng)畫頁(yè)面只有一個(gè)層,那么運(yùn)用了 transform 進(jìn)行變換,頁(yè)面必然需要重繪,但是采用分層(GraphicsLayer )技術(shù),也就是上面說符合情況的元素各自創(chuàng)建層,那么一個(gè)元素所創(chuàng)建的層運(yùn)用 transform 變換,譬如 rotate 旋轉(zhuǎn),這個(gè)時(shí)候該層的旋轉(zhuǎn)變換并沒有影響到其他層,那么該層不一定需要被重繪。(個(gè)人之見,還請(qǐng)?zhí)岢鲋刚?。
了解層的重繪對(duì) Web 動(dòng)畫的性能優(yōu)化至關(guān)重要。
是什么原因?qū)е率?invalidation)進(jìn)而強(qiáng)制重繪的呢?這個(gè)問題很難詳盡回答,因?yàn)榇嬖诖罅繉?dǎo)致邊界失效的情況。最常見的情況就是通過操作 CSS 樣式來修改 DOM 或?qū)е轮嘏拧?/p>
查找引發(fā)重繪和重排根源的最好辦法就是使用開發(fā)者工具的時(shí)間軸和 enable paint flashing 工具,然后試著找出恰好在重繪/重排前修改了 DOM 的地方。
那么瀏覽器是如何從 DOM 元素到最終動(dòng)畫的展示呢?
瀏覽器解析 HTML 獲取 DOM 后分割為多個(gè)圖層(GraphicsLayer)
對(duì)每個(gè)圖層的節(jié)點(diǎn)計(jì)算樣式結(jié)果(Recalculate style--樣式重計(jì)算)
為每個(gè)節(jié)點(diǎn)生成圖形和位置(Layout--回流和重布局)
將每個(gè)節(jié)點(diǎn)繪制填充到圖層位圖中(Paint Setup和Paint--重繪)
圖層作為紋理(texture)上傳至 GPU
符合多個(gè)圖層到頁(yè)面上生成最終屏幕圖像(Composite Layers--圖層重組)
Web 動(dòng)畫很大一部分開銷在于層的重繪,以層為基礎(chǔ)的復(fù)合模型對(duì)渲染性能有著深遠(yuǎn)的影響。當(dāng)不需要繪制時(shí),復(fù)合操作的開銷可以忽略不計(jì),因此在試著調(diào)試渲染性能問題時(shí),首要目標(biāo)就是要避免層的重繪。那么這就給動(dòng)畫的性能優(yōu)化提供了方向,減少元素的重繪與回流。
這里首先要分清兩個(gè)概念,重繪與回流。
當(dāng)渲染樹(render Tree)中的一部分(或全部)因?yàn)樵氐囊?guī)模尺寸,布局,隱藏等改變而需要重新構(gòu)建。這就稱為回流(reflow),也就是重新布局(relayout)。
每個(gè)頁(yè)面至少需要一次回流,就是在頁(yè)面第一次加載的時(shí)候。在回流的時(shí)候,瀏覽器會(huì)使渲染樹中受到影響的部分失效,并重新構(gòu)造這部分渲染樹,完成回流后,瀏覽器會(huì)重新繪制受影響的部分到屏幕中,該過程成為重繪。
當(dāng)render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀,風(fēng)格,而不會(huì)影響布局的,比如 background-color ,則稱為重繪。
值得注意的是,回流必將引起重繪,而重繪不一定會(huì)引起回流。
明顯,回流的代價(jià)更大,簡(jiǎn)單而言,當(dāng)操作元素會(huì)使元素修改它的大小或位置,那么就會(huì)發(fā)生回流。
調(diào)整窗口大小(Resizing the window)
改變字體(Changing the font)
增加或者移除樣式表(Adding or removing a stylesheet)
內(nèi)容變化,比如用戶在input框中輸入文字(Content changes, such as a user typing text in
an input box)
激活 CSS 偽類,比如 :hover (IE 中為兄弟結(jié)點(diǎn)偽類的激活)(Activation of CSS pseudo classes such as :hover (in IE the activation of the pseudo class of a sibling))
操作 class 屬性(Manipulating the class attribute)
腳本操作 DOM(A script manipulating the DOM)
計(jì)算 offsetWidth 和 offsetHeight 屬性(Calculating offsetWidth and offsetHeight)
設(shè)置 style 屬性的值 (Setting a property of the style attribute)
所以對(duì)于頁(yè)面而言,我們的宗旨就是盡量減少頁(yè)面的回流重繪,簡(jiǎn)單的一個(gè)栗子:
// 下面這種方式將會(huì)導(dǎo)致回流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 // 下面這種方式更好,只會(huì)回流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
上面四句,因?yàn)樯婕傲?offsetHeight 操作,瀏覽器強(qiáng)制 reflow 了兩次,而下面四句合并了 offset 操作,所以減少了一次頁(yè)面的回流。
減少回流、重繪其實(shí)就是需要減少對(duì)渲染樹的操作(合并多次多DOM和樣式的修改),并減少對(duì)一些style信息的請(qǐng)求,盡量利用好瀏覽器的優(yōu)化策略。
其實(shí)瀏覽器自身是有優(yōu)化策略的,如果每句 Javascript 都去操作 DOM 使之進(jìn)行回流重繪的話,瀏覽器可能就會(huì)受不了。所以很多瀏覽器都會(huì)優(yōu)化這些操作,瀏覽器會(huì)維護(hù) 1 個(gè)隊(duì)列,把所有會(huì)引起回流、重繪的操作放入這個(gè)隊(duì)列,等隊(duì)列中的操作到了一定的數(shù)量或者到了一定的時(shí)間間隔,瀏覽器就會(huì) flush 隊(duì)列,進(jìn)行一個(gè)批處理。這樣就會(huì)讓多次的回流、重繪變成一次回流重繪。
但是也有例外,因?yàn)橛械臅r(shí)候我們需要精確獲取某些樣式信息,下面這些:
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop/Left/Width/Height
clientTop/Left/Width/Height
width,height
請(qǐng)求了getComputedStyle(), 或者 IE的 currentStyle
這個(gè)時(shí)候,瀏覽器為了反饋?zhàn)罹_的信息,需要立即回流重繪一次,確保給到我們的信息是準(zhǔn)確的,所以可能導(dǎo)致 flush 隊(duì)列提前執(zhí)行了。
兩者都可以在頁(yè)面上隱藏節(jié)點(diǎn)。不同之處在于,
display:none 隱藏后的元素不占據(jù)任何空間。它的寬度、高度等各種屬性值都將“丟失”
visibility:hidden 隱藏的元素空間依舊存在。它仍具有高度、寬度等屬性值
從性能的角度而言,即是回流與重繪的方面,
display:none 會(huì)觸發(fā) reflow(回流)
visibility:hidden 只會(huì)觸發(fā) repaint(重繪),因?yàn)闆]有發(fā)現(xiàn)位置變化
他們兩者在優(yōu)化中 visibility:hidden 會(huì)顯得更好,因?yàn)槲覀儾粫?huì)因?yàn)樗ジ淖兞宋臋n中已經(jīng)定義好的顯示層次結(jié)構(gòu)了。
對(duì)子元素的影響:
display:none 一旦父節(jié)點(diǎn)元素應(yīng)用了 display:none,父節(jié)點(diǎn)及其子孫節(jié)點(diǎn)元素全部不可見,而且無論其子孫元素如何設(shè)置 display 值都無法顯示;
visibility:hidden 一旦父節(jié)點(diǎn)元素應(yīng)用了 visibility:hidden,則其子孫后代也都會(huì)全部不可見。不過存在隱藏“失效”的情況。當(dāng)其子孫元素應(yīng)用了 visibility:visible,那么這個(gè)子孫元素又會(huì)顯現(xiàn)出來。
不同樣式在消耗性能方面是不同的,譬如 box-shadow 從渲染角度來講十分耗性能,原因就是與其他樣式相比,它們的繪制代碼執(zhí)行時(shí)間過長(zhǎng)。這就是說,如果一個(gè)耗性能嚴(yán)重的樣式經(jīng)常需要重繪,那么你就會(huì)遇到性能問題。其次你要知道,沒有不變的事情,在今天性能很差的樣式,可能明天就被優(yōu)化,并且瀏覽器之間也存在差異。
因此關(guān)鍵在于,你要借助開發(fā)工具來分辨出性能瓶頸所在,然后設(shè)法減少瀏覽器的工作量。
好在 chrome 瀏覽器提供了許多強(qiáng)大的功能,讓我們可以檢測(cè)我們的動(dòng)畫性能,除了上面提到的,我們還可以通過勾選下面這個(gè) show FPS meter 顯示頁(yè)面的 FPS 信息,以及 GPU 的使用率:
官方文檔說[4],這是一個(gè)仍處于實(shí)驗(yàn)階段的功能,所以在未來版本的瀏覽器中該功能的語(yǔ)法和行為可能隨之改變。
will-change 為 web 開發(fā)者提供了一種告知瀏覽器該元素會(huì)有哪些變化的方法,這樣瀏覽器可以在元素屬性真正發(fā)生變化之前提前做好對(duì)應(yīng)的優(yōu)化準(zhǔn)備工作。 這種優(yōu)化可以將一部分復(fù)雜的計(jì)算工作提前準(zhǔn)備好,使頁(yè)面的反應(yīng)更為快速靈敏。
看看 Can i Use - will-change[5],更新于 2021/03/31 :
使用方法示例:(具體每個(gè)取值的意義,去翻翻文檔)
will-change: auto will-change: scroll-position will-change: contents will-change: transform // Example of will-change: opacity // Example of will-change: left, top // Example of two will-change: unset will-change: initial will-change: inherit // 示例 .example{ will-change: transform; }
值得注意的是,用好這個(gè)屬性并不是很容易:
不要將 will-change 應(yīng)用到太多元素上:瀏覽器已經(jīng)盡力嘗試去優(yōu)化一切可以優(yōu)化的東西了。有一些更強(qiáng)力的優(yōu)化,如果與 will-change 結(jié)合在一起的話,有可能會(huì)消耗很多機(jī)器資源,如果過度使用的話,可能導(dǎo)致頁(yè)面響應(yīng)緩慢或者消耗非常多的資源。
有節(jié)制地使用:通常,當(dāng)元素恢復(fù)到初始狀態(tài)時(shí),瀏覽器會(huì)丟棄掉之前做的優(yōu)化工作。但是如果直接在樣式表中顯式聲明了 will-change 屬性,則表示目標(biāo)元素可能會(huì)經(jīng)常變化,瀏覽器會(huì)將優(yōu)化工作保存得比之前更久。所以最佳實(shí)踐是當(dāng)元素變化之前和之后通過腳本來切換 will-change 的值。
不要過早應(yīng)用 will-change 優(yōu)化:如果你的頁(yè)面在性能方面沒什么問題,則不要添加 will-change 屬性來榨取一丁點(diǎn)的速度。will-change 的設(shè)計(jì)初衷是作為最后的優(yōu)化手段,用來嘗試解決現(xiàn)有的性能問題。它不應(yīng)該被用來預(yù)防性能問題。過度使用 will-change 會(huì)導(dǎo)致大量的內(nèi)存占用,并會(huì)導(dǎo)致更復(fù)雜的渲染過程,因?yàn)闉g覽器會(huì)試圖準(zhǔn)備可能存在的變化過程。這會(huì)導(dǎo)致更嚴(yán)重的性能問題。
給它足夠的工作時(shí)間:這個(gè)屬性是用來讓頁(yè)面開發(fā)者告知瀏覽器哪些屬性可能會(huì)變化的。然后瀏覽器可以選擇在變化發(fā)生前提前去做一些優(yōu)化工作。所以給瀏覽器一點(diǎn)時(shí)間去真正做這些優(yōu)化工作是非常重要的。使用時(shí)需要嘗試去找到一些方法提前一定時(shí)間獲知元素可能發(fā)生的變化,然后為它加上 will-change 屬性。
3D transform 會(huì)啟用GPU加速,例如 translate3D, scaleZ 之類,當(dāng)然我們的頁(yè)面可能并沒有 3D 變換,但是不代表我們不能啟用 GPU 加速,在非 3D 變換的頁(yè)面也使用 3D transform 來操作,算是一種 hack 加速法。我們實(shí)際上不需要z軸的變化,但是還是假模假樣地聲明了,去欺騙瀏覽器。
參考文獻(xiàn):
Rendering: repaint, reflow/relayout, restyle[6]
Scrolling Performance[7]
MDN--will-change[8]
How (not) to trigger a layout in WebKit[9]
High Performance Animations[10]
Accelerated Rendering in Chrome[11]
CSS3 制作3D旋轉(zhuǎn)球體[12]
“CSS3 3D行星運(yùn)轉(zhuǎn)與瀏覽器渲染的原理是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。