一種新的骨架屏方案

2023-03-13 09:00:18

動機

最近遇到了一個實現骨架屏的需求,我大致調研了一下市面上使用得比較多的骨架屏方案,發現這些方案很多都不能滿足我的需求,比如

或者

從第一幅圖可以看到,骨架屏中的元素有著各自獨立的動畫效果,也就是說其中的「波浪」並不是從左邊的元素跑到右邊的元素,而是分別從每個元素的左邊跑到每個元素的右邊,這看起來多少有點不太優雅。

第二個例子為了避免圖一中暴露的問題改用了整體色彩漸變的方式,通過去掉「波浪」效果使得元素的動畫效果看起來是統一的,但這樣顯然讓頁面少了些「靈動」。

另外,這兩個骨架屏元件在實現上都採用了類似的介面

可以看出,這樣的介面設計,基本上決定了骨架屏中的元素只能是圓形或矩形,而且排版方式相對固定。同時動畫效果也比較固定,只有漸變和「波浪」兩種。

還有一種骨架屏方案是用 svg 的 clipPath 實現的,它基本上解決了上述的問題,但缺點是用起來有些門檻。很多前端同學其實並沒有自己寫過 svg,對 svg 技術的用法和特點也不太熟悉,所以這個方案的作者甚至專門實現了一個生成器工具,讓使用者通過 gui 介面來生成一套使用這個骨架屏元件的設定,可見使用門檻並不低。

那麼有沒有一種方法既能做到優雅的效果和靈活的設定介面,同時還方便使用能夠快速上手呢?

方案

首先,一個 css 動畫效果只能在單一 dom 節點上執行,所以要想實現「波浪」效果在元素之間流轉,給每個元素單獨設定動畫效果是肯定行不通的。所以為了實現一個能在元素間流轉的「波浪」,我們給骨架屏一個撐滿的背景元素,並在背景元素上執行「波浪」動畫效果,再通過某些方式實現一個帶鏤空視窗的蒙板。

要實現一個帶鏤空視窗的蒙板,最簡單直觀的方案,自然是用若干個不透明的元素把不需要鏤空的位置遮擋起來,留出需要鏤空的地方。

這樣對於矩形鏤空區域還比較好處理,如果要求鏤空區域是圓形或其他複雜形狀就難以實現了。而且從上圖就能看出,這樣的方案在具體實現時會非常麻煩,尤其是當骨架屏元素的尺寸需要根據外面環境相應變化的時候,這種方案的實現難度就會變得更高,程式碼也難以維護。

那麼,如果實現一個簡單又易於維護的蒙板呢?

到這裡,就要引入我們本篇的重點了:mix-blend-mode

mix-blend-mode 是用來控制圖層色彩混合方式的,一共有 16 種模式,我們著重講一下這其中的 darkenlighten 兩種模式。這兩種混色模式的邏輯可以參考這個連結,可以看到這兩種模式的演演算法其實都非常簡單,darken 模式下混色時直接對兩種顏色分別在 R、G、B 三個通道取較小值,相反 lighten 模式下則是對兩種顏色分別在 R、G、B 三個通道取較大值。

那麼考慮上圖的場景,上層的圖層外圍是白色,中間的矩形是黑色。這時如果給這個圖層設定 mix-blend-modelighten,那麼對於外圍白色的區域由於白色在 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