神奇!探究混合模式\濾鏡導(dǎo)致 3D 失效問題
之前在寫一個小的 CSS Demo,一個關(guān)于 3d 球的旋轉(zhuǎn)動畫,關(guān)于 CSS 3D,少不了會使用下面這幾個屬性:
{
transform-style: preserve-3d;
perspective: 1000;
transform: translate3d();
}
這個 Demo 你可以戳這里,大概是這樣:CodePen Demo - 3D ball[1]:
嗯,大概到了這個效果,想到了 CSS 混合模式 mix-blend-mode,尋思著,利用混合模式,是否能讓效果更上一層樓或者碰撞出一些其他火花。
mix-blend-mode?:我們通常稱之為混合模式,利用混合模式將多個圖層混合可以得到一個新的效果,mix-blend-mode 描述了元素的內(nèi)容應(yīng)該與元素的直系父元素的內(nèi)容和元素的背景如何混合。
關(guān)于混合模式的一些使用可以看這里:不可思議的混合模式 background-blend-mode (二)[2]、不可思議的混合模式 background-blend-mode[3]。
CSS 3D 配合 mix-blend-mode
然而,給元素加上了一個混合模式之后,神奇的事情發(fā)生了,3D 效果消失了。
也就是在每個光點的 CSS 元素代碼中添加這樣一句:
{
mix-blend-mode: lighten;
}
效果從 CSS 3D 變成了 2D。
這就很蹊蹺了,預(yù)想中的混合并沒有發(fā)生,取而代之的是 3D 的失效。我想,也許與內(nèi)核有關(guān),上面的效果是在最新的 chrome 上試驗得到的。
是否與瀏覽器內(nèi)核有關(guān)?(截止至 2022-09-13)
帶著這樣的疑問,我又測試了下其他幾個內(nèi)核:
- firefox 104.0 -- 同 Chrome,3D 退化為 2D。
- Safari 15.6.1 -- 渲染正常。
Safari 是可以正常展示的,只能初略的認(rèn)為,應(yīng)該是與內(nèi)核有關(guān)系的。那應(yīng)該也有很多人遇到過同樣的問題,帶著這個疑惑,google 一下。
爆棧網(wǎng)也有同學(xué)提出類似的疑惑:StackOverflow -- mix-blend-mode is broken by 3D transformations on page[4]。
隨后,在 chromium bug 提交網(wǎng)站上,找到了 15 年的一個 bug 單,也是對這個問題的疑問:
BUG -CSS mix-blend-mode turns off CSS perspective.[5]。
最終在 bug 單的最下面找到了可能靠譜的回答:
When we have mix-blend-mode, the closest ancestor that creates stacking context will isolate blending. We create a render surface at the root of this isolated group and because render surfaces don't support preserve-3d(because they render into separate FBO), we see a flattened result.
ajuma@ suggested that this bug maybe much easier to fix after Slimming paint v2 if we can somehow disentangle transforms from layers。
翻譯一下,意思大概是:當(dāng)我們使用 CSS 混合模式的時候,堆疊上下文會重新對這個使用了混合模式的元素的根節(jié)點處創(chuàng)建一個獨立的渲染平面,但是很可惜,這個渲染平面是不支持 preserve-3d 的(因為它們渲染到單獨的FBO中),所以我們看到是一個 2D 的平面效果。
驗證 Layer borders
上面的那句話應(yīng)該已經(jīng)可以作為結(jié)論,我再使用 chrome 提供的工具驗證一下,打開開發(fā)者工具的 Rendering -> Layer borders:
黃色代表 CSS 渲染時候的 GraphicsLayer 層, 藍色網(wǎng)格表示瓦片(tile),你可以把它們當(dāng)作是層的單元(并不是層),Chrome 內(nèi)核可以將它們作為一個大層的部分上傳給 GPU 進行渲染加速。
- 正常 3D 模式下,開啟 Layer borders 效果:
balllayer
- 添加了mix-blend-mode 的 3D 模式下,開啟 Layer borders 效果:
可以看到,在 mix-blend-mode 的 3D 模式下,確實在整個球形元素之外,又多了一層藍色 tile。也就是上文提到的獨立的渲染平面,也就是因為這個渲染平面不支持 preserve-3d 的原因,我們最終得到了一個 2D 平面圖形。
濾鏡也會導(dǎo)致 CSS 3D 失效
完了嗎?沒有。不是吧,這誰頂?shù)米“ ?/p>
那么如果是因為 mix-blend-mode 多生成了一個獨立渲染平面導(dǎo)致的 3D 失效,那么是否有其他元素也會導(dǎo)致同樣的結(jié)果呢?
帶著疑惑,去掉了 mix-blend-mode,我又給設(shè)置了 3d 的元素添加了一個濾鏡:
{
- mix-blend-mode: lighten;
+ filter: blur(1px);
}
果然,出現(xiàn)了同樣的問題,3D 失效:
balllayer3
總結(jié)一下
嗯。那么應(yīng)該可以初步得到一個結(jié)論就是所有這些在渲染時候需要再獨立生成一個渲染平面,且包含了 preserve-3d 的屬性,都會導(dǎo)致內(nèi)部的 CSS 3D 失效。
暫時我發(fā)現(xiàn)的有下述幾個屬性,都會導(dǎo)致 CSS 3D 失效:
- mix-blend-mode
- background-blend-mode
- filter
其他問題
這個 bug 有什么影響
額,通常來說,很少會有人在使用 CSS 3D 的同時使用混合模式或者濾鏡,這兩個屬性更多的錦上添花的作用,所以大部分時候,不使用它們就不會有問題, 所以影響不是很大。
上文中的 FBO 是什么?
上文的 FBO 準(zhǔn)確而言是什么我也無法 100% 確定,推測應(yīng)該是 Frame Buffer Object,幀緩存對象,存在于顯存中。幀緩存是一些二維數(shù)組和 OpenGL 所使用的存儲區(qū)的集合:顏色緩存、深度緩存、模板緩存和累計緩存。
各種三維場景現(xiàn)在渲染到屏幕上都是先放到一個 FBO 中,可以理解為一張離屛圖片,用于加速渲染。
Bug 何時會被修復(fù)
在 chromium bugs 網(wǎng)站,上述 bug 被合并到 issue 575099[6],并且最終狀態(tài)是 Untriaged,表示尚未分配優(yōu)先級,意思是等待某人確定哪個人應(yīng)該認(rèn)領(lǐng)并修復(fù)該特定錯誤。所以,短期內(nèi)可能無望解決。
最后
好了,本文到此結(jié)束,希望對你有幫助 :)
參考資料
[1]CodePen Demo - 3D ball: https://codepen.io/Chokcoco/pen/JwdvmJ
[2]不可思議的混合模式 background-blend-mode (二): https://github.com/chokcoco/iCSS/issues/31
[3]不可思議的混合模式 background-blend-mode: https://github.com/chokcoco/iCSS/issues/16
[4]StackOverflow -- mix-blend-mode is broken by 3D transformations on page: https://stackoverflow.com/questions/32932966/mix-blend-mode-is-broken-by-3d-transformations-on-page
[5]BUG -CSS mix-blend-mode turns off CSS perspective.: https://bugs.chromium.org/p/chromium/issues/detail?id=543445
[6]issue 575099: https://bugs.chromium.org/p/chromium/issues/detail?id=575099