使用CSS自定義屬性構建骨架屏

codercao發表於2019-03-04

寫在前面

幾天前看到薄荷前端團隊分享的《前端骨架屏方案小結》,突然回想起一年前看到的max bock寫的《Building Skeleton Screens with CSS Custom Properties》,翻譯整理寫下出此文,分享一下使用CSS自定義屬性構建骨架屏的技巧,先看骨架屏demo效果吧

使用CSS自定義屬性構建骨架屏

設計Web上的載入狀態常常被忽略或被認為是事後考慮。效能不僅是前端開發人員的職責,構建與慢速連線一起工作的體驗也是設計挑戰。

雖然前端開發人員需要注意一些事情,比如壓縮和快取,但是設計人員必須考慮UI處於“載入”或“離線”狀態時的外觀和行為。

速度幻覺

隨著我們對移動體驗的期望發生變化,我們對效能的理解也在變化。我們期望網路應用程式感覺像本機應用程式一樣快速響應,無論其當前的網路覆蓋範圍如何。

感知效能是衡量使用者感覺速度的尺度。這個想法是使用者更有耐心,並且如果他們知道正在發生什麼,並且在內容實際存在之前能夠預測內容,那麼他們會認為系統更快。這在很大程度上與管理期望和保持使用者知情有關。

對於Web應用程式,這個概念可能包括顯示文字,影像或其他內容元素的“模型” – 稱為
骨架屏?。可以在網上可以看到,Facebook,Google,Slack等公司使用:

使用CSS自定義屬性構建骨架屏(Facebook的骨架屏)

使用CSS自定義屬性構建骨架屏

(Slack的骨架屏)

例子

假設你正在構建一個Web應用程式,這是一種旅行建議型別的東西,人們可以分享他們的旅行和推薦地點,所以你的主要內容可能看起來像這樣:

使用CSS自定義屬性構建骨架屏

您可以將該卡片縮小到其基本視覺形狀(UI元件的骨架)

使用CSS自定義屬性構建骨架屏

每當有人從伺服器請求新內容時,您可以立即開始顯示骨架,同時在後臺載入資料。內容準備就緒後,只需將骨架換成實際卡即可。這可以使用普通的JavaScript或使用像Vue/React這樣的庫來完成。
現在我們可以使用影像來顯示骨架,但這會引入額外的請求和資料開銷。我們已經在這裡載入了東西,所以等待另一個影像首先載入並不是一個好方式。此外,它沒有響應,如果我們決定調整一些內容卡的樣式,我們將不得不復制骨架影像的更改,以便它們再次匹配。?
一個更好的解決方案是隻用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

使用CSS自定義屬性構建骨架屏

當然你可以使用:empty選擇器和偽元素來繪製骨架,因此它只適用於空卡片元素,一旦注入了內容,框架螢幕就會自動消失

最後,感興趣的同學可去我github下載這個骨架屏-demo原始碼傳送門

相關文章