fixed 定位失效 與 CSS 層疊上下文

第一部分,position: fixed失效的問題;
第二部分,瞭解一下由此扯出的一個Stacking Context層疊上下文。
文中大部分內容來自於我對W3C文檔及一些英文資料的理解,如有不明確或表述不準確的地方還請指出。

fixed 定位與 CSS 層疊上下文的關係

關於 position: fixed

position: fixed 在日常佈局中比較常用,如移動端頭部和底部導航定位、模態框、懸浮按鈕等,設置了這個屬性值得元素會相對於屏幕視口(viewport)進行定位,其位置在屏幕進行滾動時會保持不變,不佔用文檔流中的位置,而且打印時這個元素會出現在 每一頁 的相同位置。設置了 position: fixed 的元素最終的位置由它的 top, right, bottom, left 來決定,這個值會創建一個新的 stacking context(不知道這個詞應該如何準確地翻譯,姑且認爲是層疊上下文吧)。
但是,有些情況下,這種定位方式會失效,使得元素相對於視窗定位的定位不符合預期(其實是 fixed 定位的參考元素變了)。當該元素的父元素中(廣義,包含祖先元素)有元素的 transformperspective 的值不是 none,該元素就會相對於這個父元素而不是視口進行定位。
具體的原因是這樣:

Specifying a value other than none for the transform property establishes a new local coordinate system at the element that it is applied to. The mapping from where the element would have rendered into that local coordinate system is given by the element’s transformation matrix. Transformations are cumulative. That is, elements establish their local coordinate system within the coordinate system of their parent. From the perspective of the user, an element effectively accumulates all the transform properties of its ancestors as well as any local transform applied to it. The accumulation of these transforms defines a current transformation matrix for the element.

解釋一下,transformperspective 的非 none 值會影響元素的包含塊和層疊上下文,這些值會在應用它的元素上建立一個局部的座標系(X軸向右水平增加; Y軸垂直向下增加),由變換矩陣(transform 的值)給出元素到該局部座標系的映射,而且 transform 帶來的局部座標系的改變是可以累積的——也就是說,子元素會在它的父元素的座標系內建立子元素自己的局部座標系:
父元素的 transform 們一層層積累定義了子元素當前的變換矩陣(一個元素的變換矩陣是從 transformtransform-origin 屬性中計算出來的),步驟如下:

  • 通過 transform-origin 的值對座標原點 XY 的位置進行轉換
  • 以變換後的 XY 爲座標原點原點,根據 transform 的屬性值進行變換
  • XY 根據 transform-origin 的相反值平移回去

如圖:

transform 計算過程

transform 會影響最終的渲染效果,但是不影響除overflow外的CSS佈局,當通過 getClientRects()getBoundingClientRect() 這些接口獲取 client rectangles 時,transform 的效果也會被考慮進去。

可以應用 transform 的元素(transformable elements)有:
- 滿足CSS盒模型的塊級元素或行內元素,或者它的 display 值爲 table-row, table-row-group, table-header-group, table-footer-group, table-cell, table-caption 中的一個
- SVG 命名空間中具有 transform, patternTransformgradientTransform 屬性的元素

p.s. 關於上面的應用transform後元素位置的計算方式,是我按照自己的理解翻譯了原文的內容,如有不準確的地方還請指出。原文如下:
>
1. Start with the identity matrix.
2. Translate by the computed X and Y of transform-origin
3. Multiply by each of the transform functions in transform property from left to right
4. Translate by the negated computed X and Y values of transform-origin

參考文檔:

The transform Property - W3C Working Draft - 30 November 2017

The Transform Rendering Model - W3C Working Draft - 30 November 2017

層疊上下文 Stacking Context

通過上文我們知道了有層疊上下文這麼一個東西,層疊上下文是HTML元素的三維概念,這些HTML元素在一條假想的相對於面向(電腦屏幕的)視窗z軸上延伸,HTML元素依據其自身屬性按照優先級順序佔用層疊上下文的空間。和BFC還有IFC這些xx context一樣,創建層疊上下文也是有條件的,文檔中的層疊上下文由滿足以下任意一個條件的元素形成:

  • 根元素 (HTML),
  • z-index 值不爲 auto 的 絕對/相對定位元素
  • position 不是 static 的元素(sticky 也會創建層疊上下文,這是一個神奇的實驗中的屬性值)
  • 一個 z-index 值不爲 auto 的 flex 項目 (flex item)
  • opacity 屬性值小於 1 的元素(參考 the specification for opacity
  • transform 屬性值不爲 none 的元素
  • mix-blend-mode 屬性值不爲 normal 的元素
  • transformfilterperspectiveclip-pathmask / mask-image / mask-border 這些屬性中任意一個或多個屬性的元素
  • isolation 屬性被設置爲 isolate的元素
  • will-change 中指定了任意CSS` 屬性的元素(即使沒有直接指定這些屬性的值)
  • -webkit-overflow-scrolling 屬性被設置 touch的元素
  • 設置了 transform-style: preserve-3d 的元素

在層疊上下文中的子元素也會按照上面的規則進行層疊,子元素的 z-index 值只在父級層疊上下文中有意義,子級層疊上下文被自動視爲父級層疊上下文的一個獨立單元

但是,並不是創建了新的層疊上下文的元素並不一定都會對其擁有position: fixed的子元素的效果產生影響,
在Chrome(Blink內核)中,可以明確看到產生了影響的是:

  • transform 屬性值不爲 none 的元素
  • 設置了 transform-style: preserve-3d 的元素
  • perspective 值不爲 none 的元素
  • will-change 中指定了任意 CSS 屬性的元素

但是,在不同的瀏覽器內核下,上述結論也會有所差異,例如,在 Safari(Webkit內核) 中,只有transform 屬性值不爲 none 的元素會對 fixed 定位的效果產生影響.

參考文檔:

the specification for opacity - W3C

The stacking context - MDN

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章