動機
最近遇到了一個實現骨架屏的需求,我大致調研了一下市面上使用得比較多的骨架屏方案,發現這些方案很多都不能滿足我的需求,比如
或者
從第一幅圖可以看到,骨架屏中的元素有著各自獨立的動畫效果,也就是說其中的“波浪”並不是從左邊的元素跑到右邊的元素,而是分別從每個元素的左邊跑到每個元素的右邊,這看起來多少有點不太優雅。
第二個例子為了避免圖一中暴露的問題改用了整體色彩漸變的方式,透過去掉“波浪”效果使得元素的動畫效果看起來是統一的,但這樣顯然讓頁面少了些“靈動”。
另外,這兩個骨架屏元件在實現上都採用了類似的介面
可以看出,這樣的介面設計,基本上決定了骨架屏中的元素只能是圓形或矩形,而且排版方式相對固定。同時動畫效果也比較固定,只有漸變和“波浪”兩種。
還有一種骨架屏方案是用 svg 的 clipPath 實現的,它基本上解決了上述的問題,但缺點是用起來有些門檻。很多前端同學其實並沒有自己寫過 svg,對 svg 技術的用法和特點也不太熟悉,所以這個方案的作者甚至專門實現了一個生成器工具,讓使用者透過 gui 介面來生成一套使用這個骨架屏元件的配置,可見使用門檻並不低。
那麼有沒有一種方法既能做到優雅的效果和靈活的配置介面,同時還方便使用能夠快速上手呢?
方案
首先,一個 css 動畫效果只能在單一 dom 節點上執行,所以要想實現“波浪”效果在元素之間流轉,給每個元素單獨設定動畫效果是肯定行不通的。所以為了實現一個能在元素間流轉的“波浪”,我們給骨架屏一個撐滿的背景元素,並在背景元素上執行“波浪”動畫效果,再透過某些方式實現一個帶鏤空視窗的蒙板。
要實現一個帶鏤空視窗的蒙板,最簡單直觀的方案,自然是用若干個不透明的元素把不需要鏤空的位置遮擋起來,留出需要鏤空的地方。
這樣對於矩形鏤空區域還比較好處理,如果要求鏤空區域是圓形或其他複雜形狀就難以實現了。而且從上圖就能看出,這樣的方案在具體實現時會非常麻煩,尤其是當骨架屏元素的尺寸需要根據外面環境相應變化的時候,這種方案的實現難度就會變得更高,程式碼也難以維護。
那麼,如果實現一個簡單又易於維護的蒙板呢?
到這裡,就要引入我們本篇的重點了:mix-blend-mode。
mix-blend-mode
是用來控制圖層色彩混合方式的,一共有 16 種模式,我們著重講一下這其中的 darken
和 lighten
兩種模式。這兩種混色模式的邏輯可以參考這個連結,可以看到這兩種模式的演算法其實都非常簡單,darken 模式下混色時直接對兩種顏色分別在 R、G、B 三個通道取較小值,相反 lighten 模式下則是對兩種顏色分別在 R、G、B 三個通道取較大值。
那麼考慮上圖的場景,上層的圖層外圍是白色,中間的矩形是黑色。這時如果給這個圖層設定 mix-blend-mode
為 lighten
,那麼對於外圍白色的區域由於白色在 R、G、B 三個通道都已經是最大值了,所以還是會保持白色。而中間黑色的矩形區域,由於黑色在 R、G、B 三個通道都已經是最小值了,所以就會變成下面圖層的顏色。最終就是下面這樣的效果:
到這裡,我們就已經可以透過 mix-blend-mode
實現了一個簡單的骨架屏效果了!
更進一步,如果要求骨架屏的非動畫元素部分不是白色,該怎麼辦呢?可能你已經想到了,我們用 mix-blend-mode
再前面的基礎上再取一次色。
如上圖所示,我們再構建一個“蒙板”,這次我們把最上層的外圍設定為需求要求的骨架屏外圍顏色,把中間的矩形區域設定成白色,然後再給這個圖層的 mix-blend-mode
設定為 darken
,這樣外圍區域因為下層的白色已經在 R、G、B 三個通道都已經是最大值了,所以用 darken
模式混色時就會取到最上層的顏色。而中間的矩形區域也因為同樣的原因會渠道下層矩形區域的顏色。
由此,一個可以任意定義色彩範圍的骨架屏元件就實現了。
總結
透過上面的示例,你應該可以發現,用 mix-blend-mode
來實現蒙板效果相比於文件開頭提到的那幾種方案,不僅功能上靈活多樣,而且上手非常簡單,前端開發人員只用寫基本的 DOM 結構和樣式就能快速得到自己想要的效果。
而且,基於 mix-blend-mode
的蒙板,還可以用在很多其他的場景,比如下面這個效果,選項的邊框和選項內的金額字元的漸變效果是連續的,不使用蒙板的話只能讓設計師直接出圖來實現了,但這樣在國際化、黑暗模式、多解析度適配等場景下就會增加很多複雜度,而使用蒙版來實現就會變得非常簡單。
相關程式碼我已經封裝成了一個 npm 包:skeleton-screen-react
歡迎 star、issue、pr