使用 CSS 自定義屬性實現骨架屏
原文地址:https://css-tricks.com/building-skeleton-screens-css-custom-properties/
原文作者:Tapas Adhikary
譯者:陽光是 sunny
1 前言
其實這篇文章網上已經有翻譯版本,但是讀起來明顯是機翻的,實在是受不了,於是就用自己的理解翻譯了一下
2 正文
項目要不要加載 loading 狀態通常是在項目完成後才考慮的事情,當然,有時候直接就不考慮了。
開發人員的職責不只是提高性能,同時優化網絡差時,請求接口緩慢導致的頁面的慢渲染也是非常重要的。
3 速度的錯覺
隨着我們對移動體驗的期望的變化,我們對性能的理解也在變化。我們期望,無論當前的網絡如何,web 頁面都能像原生應用程序一樣順滑,一樣快速響應。
骨架屏的出現。這個想法使得用戶更有耐心,因爲他們知道正在發生什麼,並且在內容實際存在之前能夠預測內容,那麼他們會認爲系統更快。這在很大程度上保持了用戶等待的熱情。
骨架屏💀
這個概念可能包括顯示文本,圖像或其他內容元。可以在網上可以看到骨架屏的使用已經非常廣泛,Facebook,Google,Slack 等公司都在使用。
4 舉個例子
假設你正在構建一個旅行相關的 Web 應用程序,用戶可以分享他們的旅行以及推薦的地點,它的主要內容可能看起來像這樣:
您可以將該卡片簡化到其基本視覺形狀(UI 組件的骨架)
每當有人從服務器請求新內容時,您可以立即開始顯示骨架,同時在後臺加載數據。內容準備就緒後,只需將骨架換成實際卡即可。
您可以使用圖像來顯示骨架,但這會引入額外的請求和數據開銷。我們本身已經在這裏加載了東西,所以還要去等待另一個圖像先加載,這可不是一個好主意。另外圖片不是響應式的,如果我們決定調整卡片的樣式,我們將不得不更改骨架圖像,以便它們再次匹配。😒。
一個更好的解決方案是隻用 CSS 創建骨架屏。沒有額外的請求,最小的開銷。而且以後修改更加的方便快捷。
5CSS 中繪製骨架
首先,我們需要繪製構成卡片骨架的基本形狀。
我們可以通過向background-image
屬性添加不同的漸變來做到這一點。默認情況下,線性漸變從上到下運行,具有不同的顏色過渡。如果我們只定義一個色標,其餘的保持透明,我們就可以繪製形狀。
請記住,多個背景圖像在這裏堆疊在一起,因此順序很重要。最後一個漸變定義將展示在後面,最先定義的展示在前面。
.skeleton {
background-repeat: no-repeat;
background-image:
/* layer 2: avatar */
/* white circle with 16px radius */
radial-gradient(circle 16px, white 99%, transparent 0),
/* layer 1: title */
/* white rectangle with 40px height */
linear-gradient(white 40px, transparent 0),
/* layer 0: card bg */
/* gray rectangle that covers whole element */
linear-gradient(gray 100%, transparent 0);
}
這些元素通過拉伸來填充整個空間,就像常規的塊級元素一樣。如果我們想要改變它,我們必須爲它們定義明確的尺寸。background-size 的值來設置每個圖層的寬度和高度,background-size 的值的順序保持我們使用的 background-image 順序相同
.skeleton {
background-size:
32px 32px, /* 頭像 */
200px 40px, /* 標題 */
100% 100%; /* 卡片背景 */
}
最後一步是將元素定位在卡片上。這與 position:absolute 類似,跟它的 left 和 top 屬性的值一樣。例如:我們可以給頭像和標題 模擬 padding:24px,以匹配真實卡片的外觀。
.skeleton {
background-position:
24px 24px, /* 頭像 */
24px 200px, /* 標題 */
0 0; /* 卡片背景 */
}
6 使用自定義屬性
如果我們想構建一些稍微複雜一點的東西,CSS 很快就會變得混亂並且很難閱讀。如果將代碼交給其他開發人員,他們將不知道所有這些神奇數字的來源。維護它肯定會很糟糕。
值得慶幸的是,我們現在可以使用 CSS 自定義屬性, 以更簡潔、對開發人員更友好的方式來編寫骨架樣式。
.skeleton {
/*
define as separate properties
*/
--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
);
/*
now we can break the background up
into individual shapes
*/
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;
}
這不僅更具可讀性,而且以後更改一些值也更容易。另外,我們可以使用一些變量(像 --avatar-size、--card-padding 等)來定義實際卡片的樣式,並始終使其與骨架版本保持同步。
添加一個媒體查詢來調整不同斷點的部分骨架現在也很簡單:
@media screen and (min-width: 47em) {
:root {
--card-padding: 32px;
--card-height: 360px;
}
}
瀏覽器對自定義屬性的支持很好,但不是 100%。基本上,所有現代瀏覽器都支持,IE/Edge 有點晚了。對於這個特定的用例,很容易使用 Sass 變量添加回退。
7 添加動畫
爲了使它更好,我們可以爲我們的骨架設置動畫,讓它看起來更像一個加載指示器。我們需要做的就是在頂層放置一個新的漸變,然後用@keyframes
.
這是完成骨架卡外觀的完整示例:
可以查看預覽:https://codepen.io/mxbck/pen/EvmLVp
<div class="card"></div>
/*
* Variables
*/
:root {
--card-padding: 24px;
--card-height: 340px;
--card-skeleton: linear-gradient(lightgrey var(--card-height), transparent 0);
--avatar-size: 32px;
--avatar-position: var(--card-padding) var(--card-padding);
--avatar-skeleton: radial-gradient(circle 16px at center, white 99%, transparent 0
);
--title-height: 32px;
--title-width: 200px;
--title-position: var(--card-padding) 180px;
--title-skeleton: linear-gradient(white var(--title-height), transparent 0);
--desc-line-height: 16px;
--desc-line-skeleton: linear-gradient(white var(--desc-line-height), transparent 0);
--desc-line-1-width:230px;
--desc-line-1-position: var(--card-padding) 242px;
--desc-line-2-width:180px;
--desc-line-2-position: var(--card-padding) 265px;
--footer-height: 40px;
--footer-position: 0 calc(var(--card-height) - var(--footer-height));
--footer-skeleton: linear-gradient(white var(--footer-height), transparent 0);
--blur-width: 200px;
--blur-size: var(--blur-width) calc(var(--card-height) - var(--footer-height));
}
/*
* Card Skeleton for Loading
*/
.card {
width: 280px; //demo
height: var(--card-height);
&:empty::after {
content:"";
display:block;
width: 100%;
height: 100%;
border-radius:6px;
box-shadow: 0 10px 45px rgba(0,0,0, .1);
background-image:
linear-gradient(
90deg,
rgba(lightgrey, 0) 0,
rgba(lightgrey, .8) 50%,
rgba(lightgrey, 0) 100%
), //animation blur
var(--title-skeleton), //title
var(--desc-line-skeleton), //desc1
var(--desc-line-skeleton), //desc2
var(--avatar-skeleton), //avatar
var(--footer-skeleton), //footer bar
var(--card-skeleton) //card
;
background-size:
var(--blur-size),
var(--title-width) var(--title-height),
var(--desc-line-1-width) var(--desc-line-height),
var(--desc-line-2-width) var(--desc-line-height),
var(--avatar-size) var(--avatar-size),
100% var(--footer-height),
100% 100%
;
background-position:
-150% 0, //animation
var(--title-position), //title
var(--desc-line-1-position), //desc1
var(--desc-line-2-position), //desc2
var(--avatar-position), //avatar
var(--footer-position), //footer bar
0 0 //card
;
background-repeat: no-repeat;
animation: loading 1.5s infinite;
}
}
@keyframes loading {
to {
background-position:
350% 0,
var(--title-position),
var(--desc-line-1-position),
var(--desc-line-2-position),
var(--avatar-position),
var(--footer-position),
0 0
;
}
}
/*
* Demo Stuff
*/
body {
min-height:100vh;
background-color:#FFF;
display:flex;
justify-content:center;
align-items:center;
}
程序員成長指北 專注 Node.js 技術棧分享,從 前端 到 Node.js 再到 後端數據庫,祝您成爲優秀的高級 Node.js 全棧工程師。一個有趣的且樂於分享的人。座右銘:今天未完成的,明天更不會完成。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Yqc-YoYouh9WiwW7gNRj3w