z-index
和transform
是CSS中的屬性,但不多同窗將兩者聯繫到一塊兒,感受他們八杆子打不上。事實真的是這樣嗎?若是你也不能確認,這篇文章就值得你花點時間閱讀。由於閱讀完了,你會有所收穫的。css
在開始今天的主題以前,先得回憶一下CSS中的Stacking Context(堆疊上下文)。由於只有瞭解清楚了這個概念,才能更好的瞭解下面的內容。html
任何HTML文檔默認的堆疊上下文都是<html>
元素。所以,除非建立新的堆疊上下文。默認狀況下,元素的堆疊順序相對於頁面內的其餘元素。在一個未作堆疊順序更換的頁面中,其順序就是根據HTML中的元素出現的前後順序來決定,先出現的在底下,後出現的在頂部。用數字來表示的話是就1,2,3,4,...,n
這樣的順序。瀏覽器
第二個div
作了一個margin-top
的-50px
,能夠看到第二個div
遮住了第一個div
。那麼怎麼才能改變默認的堆疊順序呢?微信
先把結論給你們拋出來,在CSS中可使用z-index
和transform
能夠改變元素的堆疊順序。但也可能會致使一些奇怪的狀況,好比具備較大的z-index
的元素並不老是位於具備較低z-index
元素的上方。好比,在一些狀況之下,同時使用z-index
和transform
會讓z-index
失效等。性能
CSS中會產生新的層狀況還有不少種:
當一個元素位於HTML文檔的最外層(
<html>
元素)測試當一個元素被定位了而且擁有一個
z-index
值(不爲auto
)flex當一個元素被設置了
opacity
,transforms
,filters
,css-regions
,paged media
等屬性動畫
flex item
,也就是父元素的display
設置了flex
或者inline-flex
值,早期的box
值不行ui
grid item
,也就是父元素的display
設置了grid
或者inline-grid
值3d
isolation:isolate
元素的
mix-blend-mode
值不爲normal
元素的
overflow-scrolling
值不爲touch
元素的
filter
值不爲none
元素的
perspective
值不爲none
元素的
motion-path
值不爲none
Web中的任何元素都存在於一個三維空間中,除了你們熟知的平面畫布中的x
軸和y
軸以外,還有控制第三維度的z
軸,以下圖所示:
在CSS中使用margin
,float
、offset
這些屬性,能夠控制元素在x
軸和y
軸上的表現。而z
軸上的表現形式能夠經過z-index
和transform
來控制。
前面也說了,控制z
是經過z-index
和transform
來實現的。先簡單的瞭解一下這兩種控制z
軸的方法。
經過z-index
控制z
軸,須要配合position
屬性,且position
的屬性值爲relative
、absolute
、fixed
和sticky
時。而且給z-index
顯式的設置數值,數值越大,其層級越高。簡單點說,數值越高,元素越在頂上。
transform
能夠經過它的translateZ()
來改變元素的層疊順序,其值越大,越在頂層,離屏幕越近。不過經過transform:translateZ()
改變元素z
軸的層級,必須在元素的父元素中顯示的設置transform-style: preserve-3d
或者在transform
中顯示的設置perspective()
。以下所示:
上面的示例可能還不能明顯的說明translateZ()
改變堆疊上下文z
軸的順序,由於上面的代碼有position
設置,那你要是以爲好奇,能夠看下面這個示例。
示例左邊的元素是沒有設置translateZ
,右邊的元素設置了translateZ
。
有關於z-index
和transform
更多的教程能夠閱讀下面這些文章:
特別聲明:接下來的內容挑選於@凹凸實驗室的《探究
transform
動畫元素的z-index
》一文。此文章詳細講解了transform
和z-index
在一塊兒使用將會發生的情況。
在一次需求中,須要作出三張卡牌走馬燈式滾動的效果,因爲在前面的一張卡牌須要擋住後面的卡牌,天然而然地就用 z-index
使前面的卡牌顯示在最上面,配以 transform
動畫讓「走馬燈」滾起來,在開發過程當中,在 PC 側 Chrome 中表現良好,在本人手機瀏覽器中也表現良好,最後測試時卻發現,在微信客戶端或 QQ 客戶端中打開頁面出現問題,「走馬燈」滾動時,卡牌先經過transform
就位後,才把 z-index
設置較大的卡牌置於上面,感受上很是的不流暢。
究其緣由,發現這是某些瀏覽器的渲染規則,涉及到 stacking context 的概念,transform
的元素會建立新的 DOM,層級會在普通元素的上面,除了 transform
,還有哪些狀況會建立新 stacking context呢?
下圖是對 transform
和 opacity
的測試結果:
很明顯,紅色 div
都在綠色 div
上面了,說明真的有建立了個更高層級的 stacking context。再作進一步測試,我給兩組的div
都加了 position:relative;z-index:1;
,結果綠色的都在上面了,手機微信上也同樣,這能不能說明 z-index
對層級的影響大於 transform
和 opacity
呢。
至於 transform
變換的時候會讓 z-index
「臨時失效」,其實並不是 z-index
失效了,只是 z-index
被用在不一樣的 stacking context 上,而非在默認的 context 上同等地比較層級了。因此 DOM 在 transform
的工程中,DOM 處於一個新的 stacking context 裏,z-index
也是相對於這個 stacking context 的,因此表現出來的實際是 stacking context 的層次,動畫一結束,DOM 又回到默認的 context 裏,這時的 z-index
纔是在同一個 context 上的比較。
那該用什麼方法來控制卡牌的層級,又能讓動畫流暢地表現呢,固然是 translate3d
中的 z-axis
,不少時候咱們並不知道它是用來作什麼的,日常用得最多的只是它的 x-axis
和 y-axis
,不妨先看個例子:
實際效果是,看不到它們,而後咱們再設置 perspective
爲 201px
,這時能夠很明顯地看到,.box2
佔據了整個屏幕,而.box1
寬高約爲 200px
,惟有設置 translate3d(0,0,0)
時,寬高才爲 100px
。
如今能夠來理解下 perspective
和 translate3d
的關係,perspective
能夠比做鏡頭和 DOM 的距離,實際上設置多少都沒影響,由於它經過跟 z-axis
上的數值比例來影響樣式,它更像是一個刻度,而 translate3d
的 z-axis
則表示了 DOM 和屏幕的距離。假定鏡頭跟屏幕的距離固定了,z-axis
越大,DOM 逐漸遠離屏幕,靠近鏡頭,這時 DOM 看起來也就越大,當 z-axis
大於或等於 perspective
時,DOM元素已經在咱們鏡頭的後面了,因此也就看不到它了。
如今也就好理解爲何 perspective
和 translate3d
可以影響 DOM 的層級了,它們在屏幕和鏡頭之間的距離不一樣,因此就有了層次,移動端設備很好地表現了這個結論,但在 PC 的 Chrome 上測試則否則,咱們仍須要 z-index
纔會表現出咱們須要的 層次關係。
在一些瀏覽器或設備上,當transform
和z-index
在一塊兒使用時會發生異樣,形成z-index
失靈。至於爲何會失靈,以及如何解決,這裏就很少講了。若是您對這方面的感興趣,能夠看看@張鑫旭大師寫得一篇文章《Safari 3D transform變換z-index層級渲染異常的研究》。
文章總結了兩種解決方案:
方法1:父級,任意父級,非body
級別,設置overflow:hidden
可恢復和其餘瀏覽器同樣的渲染
方法2:以毒攻毒。也可使用3D transform變換
至於怎麼使用3D Transform,你們仍是移步看張大師是怎麼分析的。
在介紹 z-index
和 translate3d
一節中,咱們也瞭解到了,有時候設置z-index
來控制z
軸並不有效,張大師文章也提到過,它們在一塊兒使用時,有時候會使用z-index
失靈。其實還有一個現象,你們可能平時並無注意到。
當你經過z-index
配合僞元素::before
或者::after
時讓其z
軸在元素的底部,特別是碰到大的元素渲染(好比全屏背景圖),會直接影響性能,特別是在移動端,會形成客戶端閃退,也就是你們所說的Crash,給用戶形成很是很差的體驗。
縮合上面的幾個現象(固然可能還有不少我本身沒有發現的),咱們能夠拋棄z-index
來控制z
軸的順序,而是直接經過transform
中的translateZ()
或者translate3d()
來控制z
軸的順序。
單獨使用z-index
或者transform
中的translateZ
、translate3d()
,或許你都不會想到他們之間有這麼多的故事,甚至更沒有想到在實際業務中經過transform
來替代z-index
來控制元素的z
軸的順序。那麼這篇文章介紹的就是這二者之間的故事,以及如何經過transform
來控制元素z
軸的順序。若是文章講解的有不對之處,或者你碰到過更奇葩的現象,以及相關的解決方案,歡迎在下面的評論中與咱們一塊兒分享。