使用CSS自定義屬性構建骨架屏
寫在前面
幾天前看到薄荷前端團隊分享的《 ofollow,noindex">前端骨架屏方案小結 》,突然想起一年前看到的max bock寫的《 Building Skeleton Screens with CSS Custom Properties 》,翻譯整理寫下出此文,分享一下使用CSS自定義屬性構建骨架屏的技巧,先看骨架屏demo效果吧
設計Web上的載入狀態常常被忽略或被認為是事後考慮。效能不僅是前端開發人員的職責,構建與慢速連線一起工作的體驗也是設計挑戰。 雖然前端開發人員需要注意一些事情,比如壓縮和快取,但是設計人員必須考慮UI處於“載入”或“離線”狀態時的外觀和行為。
速度幻覺
隨著我們對移動體驗的期望發生變化,我們對效能的理解也在變化。我們期望網路應用程式感覺像本機應用程式一樣快速響應,無論其當前的網路覆蓋範圍如何。
感知效能 是衡量使用者感覺速度的尺度。這個想法是使用者更有耐心,並且如果他們知道正在發生什麼,並且在內容實際存在之前能夠預測內容,那麼他們會認為系統更快。這在很大程度上與管理期望和保持使用者知情有關。
對於Web應用程式,這個概念可能包括顯示文字,影象或其他內容元素的“模型” - 稱為
骨架屏 :skull:。可以在網上可以看到,Facebook,Google,Slack等公司使用:
(Facebook的骨架屏)
(Slack的骨架屏)
例子
假設你正在構建一個Web應用程式,這是一種旅行建議型別的東西,人們可以分享他們的旅行和推薦地點,所以你的主要內容可能看起來像這樣:
您可以將該卡片縮小到其基本視覺形狀(UI元件的骨架)
每當有人從伺服器請求新內容時,您可以立即開始顯示骨架,同時在後臺載入資料。內容準備就緒後,只需將骨架換成實際卡即可。這可以使用普通的JavaScript或使用像Vue/React這樣的庫來完成。
現在我們可以使用影象來顯示骨架,但這會引入額外的請求和資料開銷。我們已經在這裡載入了東西,所以等待另一個影象首先載入並不是一個好方式。此外,它沒有響應,如果我們決定調整一些內容卡的樣式,我們將不得不復制骨架影象的更改,以便它們再次匹配。:unamused:
一個更好的解決方案是隻用CSS建立整個東西 。沒有額外的請求,最小的開銷,甚至沒有任何額外的標記。我們可以用下面的方式來構建它,使以後更改設計變得更容易。
通過CSS繪製骨架
首先,我們需要繪製構成卡片骨架的基本形狀。我們可以通過background-image屬性新增不同的漸變來實現這一點。預設情況下,線性漸變從上到下執行,有不同的顏色停止過渡。如果我們只定義一個顏色停止,並使其餘顏色保持透明,我們可以繪製形狀。
請記住,在這塊,多個背景影象堆疊在一起,因此順序非常重要。最後一個漸變定義在後面,第一個位於前面。
.skeleton { background-repeat: no-repeat; background-image: radial-gradient(circle 16px, white 99%, transparent 0), /* 第3層 頭像 */ linear-gradient(white 40px, transparent 0), /* 第2層 標題 */ linear-gradient(gray 100%, transparent 0); /* 第1層 卡片背景 */ } 複製程式碼
這些形狀拉伸來填充整個空間,就像常規的塊級元素一樣。如果我們想要改變它,我們必須為它們定義明確的尺寸。 background-size 的值來設定每個圖層的寬度和高度,保持我們使用的相同順序 background-image :
.skeleton { background-size: 32px 32px,/* 頭像 */ 200px 40px,/* 標題 */ 100% 100%; /* 卡片背景 */ } 複製程式碼
最後一步是將元素放在卡片上。這與position:absolute類似,表示left和top屬性的值一樣。例如,例如:我們可以給頭像和標題 模擬24px的填充,以匹配真實內容卡的外觀。
.skeleton { background-position: 24px 24px,/* 頭像 */ 24px 200px, /* 標題 */ 0 0;/* 卡片背景 */ } 複製程式碼
使用自定義屬性將其分解
這在一個簡單的例子中效果很好, 但是如果我們想要構建一些稍微複雜的東西,那麼CSS會很快變得混亂並且很難閱讀。如果程式碼交接給另外一個前端開發人員,他們就不知道所有這些神奇的數字來是從哪裡來的,顯然這是不易難維護的。
於是乎,這裡提出用自定義CSS屬性,以更加簡潔,更有利於前端開發人員的方式編寫骨架樣式 ,甚至可以考慮不同值之間的關係:
.skeleton { /* 定義單獨的屬性 */ --card-height: 340px; --card-padding:24px; --card-skeleton: linear-gradient(gray var(--card-height), transparent 0); --title-height: 32px; --title-width: 200px; --title-position: var(--card-padding) 180px; --title-skeleton: linear-gradient(white var(--title-height), transparent 0); --avatar-size: 32px; --avatar-position: var(--card-padding) var(--card-padding); --avatar-skeleton: radial-gradient( circle calc(var(--avatar-size) / 2), white 99%, transparent 0 ); /* 現在我們可以把背景分解成單獨的形狀 */ background-image: var(--avatar-skeleton), var(--title-skeleton), var(--card-skeleton); background-size: var(--avatar-size), var(--title-width) var(--title-height), 100% 100%; background-position: var(--avatar-position), var(--title-position), 0 0; } 複製程式碼
這不僅可讀性更好,而且以後更改一些值也更容易。另外,我們還可以使用一些變數(比如頭像大小、卡片填充)來定義實際卡片的樣式,並始終使其與骨架版本保持同步。新增一個媒體查詢來調整不同斷點的骨架部分現在也非常簡單:
@media screen and (min-width: 47em) { :root { --card-padding: 32px; --card-height: 360px; } } 複製程式碼
ps:瀏覽器對自定義屬性的支援很好,但不是100%。基本上,所有現代瀏覽器都有支援,IE / Edge有點晚了。對於這個特定用例,使用Sass變數很容易添加回退。
新增動畫
為了使這更好,我們可以為我們的骨架設定動畫,並使其看起來更像是一個載入指示器。
我們需要做的就是在頂層放置一個新的漸變,然後使用它來設定其位置的動畫 @keyframes
以下是成品骨架卡外觀的完整 骨架屏-demo :
當然你可以使用:empty選擇器和偽元素來繪製骨架,因此它只適用於空卡片元素。一旦注入了內容,框架螢幕就會自動消失。
最後,感興趣的同學可去我github下載這個 骨架屏-demo原始碼傳送門