本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫
前言
本文是筆者寫組件設計的第八篇文章, 今天帶大家用5分鐘實現一個極具創意的加載(loading)組件.涉及的核心知識點主要是css3相關特性, 如果大家非常熟悉,可直接跳過介紹直接看正文.
時刻問自己:是否具備創造力?
[筆記]前端組件的一般分類:
通用型組件: 比如Button, Icon等.布局型組件: 比如Grid, Layout布局等.導航型組件: 比如麵包屑Breadcrumb, 下拉菜單Dropdown, 菜單Menu等.數據錄入型組件: 比如form表單, Switch開關, Upload文件上傳等.數據展示型組件: 比如Avator頭像, Table表格, List列表等.反饋型組件: 比如Progress進度條, Drawer抽屜, Modal對話框等.其他業務類型所以我們在設計組件系統的時候可以參考如上分類去設計,該分類也是antd, element, zend等主流UI庫的分類方式.
正文
在開始組件設計之前希望大家對css3和js有一定的基礎,並了解基本的react/vue語法.我們先看看實現後的組件效果:
因為動圖體積太大,就不給大家傳gif了,接下來我們具體分析一下該組件的特點.
1. 組件設計思路
按照之前筆者總結的組件設計原則,我們第一步是要確認需求. 首先我們設計的不是後臺管理系統專用的加載動畫,而是作為一個C端產品的功用型加載動畫.我們都知道加載動畫的作用是:在用戶等待網頁時能看到有用的信息,比如網站介紹,引導, 公司信息等,緩解用戶焦慮. 作為一名產品經理或者用戶體驗師, 這種個性化的加載體驗效果往往是更好的.
而加載動畫一般會分為策略型加載動畫和通用加載動畫,通用加載動畫我就不說了,大家平時做的系統大部分應該都是通用型加載動畫. 我這裡介紹一下策略型加載動畫.
策略型加載動畫往往用在C端產品或者系統中,用來為用戶提供更多的引導信息, 當用戶首次訪問系統或者網站時, 由於某種主動型引導(網站在加載時或者切換頁面時故意給用戶看到的加載信息)或者環境原因(網絡,帶寬限制導致的加載過慢,此時出現加載動畫), 這些加載信息往往帶有某種用途,比如對於個人博客網站, 這個加載動畫可以是博主的介紹,博主的宣傳信息,github地址等, 對於企業來說,可能是某個新功能的介紹, 網站服務信息的介紹,聯繫方式等.
在了解完以上背景後, 我們來看看組件設計的線框圖:
對於react選手來說,如果沒用typescript,建議大家都用PropTypes, 它是react內置的類型檢測工具,我們可以直接在項目中導入. vue有自帶的屬性檢測方式,這裡就不一一介紹了.
通過以上需求分析, 其實一個加載動畫非常簡單, 不會涉及到太多功能, 主要在於css3動畫的使用. 具體屬性有:
加載動畫出現時的加載文本控制加載狀態的state接下來我們就來看看具體實現.
2. 基於react實現一個Loading組件
因為該組件不會涉及到太多的js代碼,主要是html和css,所以我們直接先構建組件的結構:
/**
* 骨架屏組件(SEO)
* @param {isLoading} bool 加載狀態
* @param {loadingText} string 加載時的加載文本
*/
export default function Skeleton(props) {
let { isLoading = true, loadingText = '正在為您瘋狂加載...' } = props
return isLoading ? <div className={styles.skeletonWrap}>
<div className={styles.skeletonContent} data-loadingText={loadingText}>
自定義的引導內容
</div>
</div> : null
}
自定義的引導內容這裡我就不介紹了, 主要根據不同的網站性質靈活配置.我主要介紹加載動畫部分, 其實原理也很簡單, 我們在skeletonContent元素上使用一個::after偽對象來實現窗簾動畫即可.
在實現動畫前大家最好對關鍵幀動畫有所了解,我相信大家都比較了解. 這種關窗簾動畫一種實現方式就是通過控制元素寬度, 從0到100%, 然後添加適當的要是優化即可. 動畫的代碼如下:
@keyframes spread {
0% {
width: 0;
}
100% {
width: 100%;
}
}
我們只需要在::after裡直接這樣使用就好了:
&::after {
animation: spread 18s 3s infinite;
}
這樣動畫已經做完了, 但是為了讓動畫更完整,我們還要考慮一個事實, 如果窗簾寬度從0慢慢變化的過程中, 加載動畫的文字一直保持一個顏色會很生硬, 如下圖:
所以說作為一個好的互動設計來說, 要讓交互體驗更順暢,這裡提供一種方式,就是加載的文本在窗簾寬度變化的同時,文字的透明度從0變化到1,這樣就會柔和很多, 所以動畫可以這麼改:
&::after {
color: rgba(255, 255, 255, 0);
animation: spread 18s infinite;
}
@keyframes spread {
0% {
width: 0;
color: rgba(255, 255, 255, 0);
}
100% {
width: 100%;
color: rgba(255, 255, 255, 1);
}
}
效果如下:
最後我們來實現loadingText. 這塊也涉及到一個知識點, 因為加載文本其實主要是用來修飾元素的,並沒有太多的語義化場景, 所以我們會放在::after偽對象的content裡, 但是一般content是在css裡寫的,那麼如何實現動態文本呢? 我們這裡就要採用css的屬性內容這個api. content不僅僅可以接收一個字符串,還可以接收attr這個關鍵字,關鍵字裡面的內容是元素的自定義屬性, 比如:
<div data-tip="loading"></div>
那麼我們在css裡可以通過這種方式直接使用data-tip屬性的值:
div::after{
content: attr(data-tip)
}
通過以上的方式我們可以在::after裡直接拿到data-tip的內容了, content其實還有更多的功能,比如用純css實現一個計數器,大家可以研究學習一下.
這樣,我們的Loading組件就完成了, 還有一個問題是我代碼裡的組件命名,為什麼叫骨架屏呢?其實我們只要改變內容結構, 它立馬就可以變成一個骨架屏,所以命名這塊可以按照實際需求來確定.
3. 健壯性支持
我們採用react提供的propTypes工具
import PropTypes from 'prop-types'
// ...
Skeleton.propTypes = {
isLoading: PropTypes.bool,
loadingText: PropTypes.string
}
組件完整css代碼如下:
.skeletonWrap {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(0,0,0, .6);
.skeletonContent {
position: relative;
margin: 200px auto 0;
padding: 20px;
width: 800px;
display: flex;
align-items: center;
border-radius: 8px;
overflow: hidden;
background-color: #fff;
&::after {
content: '正在為您瘋狂加載...';
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
width: 0;
height: 100%;
border-right: 2px solid #ccc;
box-shadow: 0 0 8px #000;
background: #096;
color: rgba(255, 255, 255, 0);
font-size: 24px;
white-space: nowrap;
animation: spread 18s 3s infinite;
}
@keyframes spread {
0% {
width: 0;
color: rgba(255, 255, 255, 0);
}
100% {
width: 100%;
color: rgba(255, 255, 255, 1);
}
}
.imgBox {
margin-right: 20px;
width: 400px;
.img {
width: 100%;
height: 200px;
background-color: #ccc;
}
img {
width: 100%;
}
}
.rightBox {
flex: 1;
.tit {
margin-top: 8px;
margin-bottom: 8px;
font-size: 22px;
}
.labelWrap {
span {
margin: 3px;
display: inline-block;
font-size: 12px;
padding: 2px 6px;
border-radius: 3px;
color: #fff;
background-color: #58bd6b;
}
}
.desc {
color: rgb(44, 44, 44);
font-size: 14px;
}
}
}
}
關於代碼中的css module和classnames的使用大家可以自己去官網學習,非常簡單.如果不懂的可以提問,筆者看到後會第一時間解答.
4. 使用Skeleton組件
我們可以通過如下方式使用它:
<Skeleton loadingText="玩命加載中..." />
筆者已經將實現過的組件發布到npm上了,大家如果感興趣可以直接用npm安裝後使用,方式如下:
npm i @alex_xu/xui
// 導入xui
import {
Button,
Skeleton,
Empty,
Progress,
Tag,
Switch,
Drawer,
Badge,
Alert
} from '@alex_xu/xui'
該組件庫支持按需導入,我們只需要在項目裡配置babel-plugin-import即可,具體配置如下:
// .babelrc
"plugins": [
["import", { "libraryName": "@alex_xu/xui", "style": true }]
]
npm庫截圖如下:
最後
後續筆者將會繼續實現
modal(模態窗),badge(徽標),table(表格),tooltip(工具提示條),Skeleton(骨架屏),Message(全局提示),form(form表單),switch(開關),日期/日曆,二維碼識別器組件等組件, 來復盤筆者多年的組件化之旅.