這篇文章是筆者寫組件設計的第四篇文章,之因此會寫組件設計相關的文章,是由於做爲一名前端優秀的前端工程師,面對各類繁瑣而重複的工做,咱們不該該循序漸進的去"辛勤勞動",而是要根據已有前端的開發經驗,總結出一套本身的高效開發的方法.做爲數據驅動的領導者react/vue等MVVM框架的出現,幫咱們減小了工做中大量的冗餘代碼, 一切皆組件的思想深得人心.因此, 爲了讓工程師們有更多的時間去考慮業務和產品迭代,咱們不得不掌握高質量組件設計的思路和方法.因此筆者將花時間去總結各類業務場景下的組件的設計思路和方法,並用原生框架的語法去實現各類經常使用組件的開發,但願等讓前端新手或者有必定工做經驗的朋友能有所收穫.javascript
今天要來實現一個高可定製的進度條組件,在介紹組件設計以前,咱們先牢記如下幾個原則.css
每日一學: 組件設計三原則html
若是對於react/vue組件設計原理不熟悉的,能夠參考個人上一篇文章:前端
《精通react/vue組件設計》之用純css打造類materialUI的按鈕點擊動畫並封裝成react組件vue
在開始組件設計以前但願你們對css3和js有必定的基礎.咱們先看看實現後的組件效果:java
上圖能夠知道封裝後的進度條組件經過對外暴露的接口(react/vue裏面能夠看作props屬性)能夠很快的實現多個不一樣的表現和重用.我將會使用react帶你們實現這個進度條組件, 你們不用擔憂技術棧不同,由於react實現的組件能夠很快套用於vue項目中, 因此說底層原理很是重要.因爲組件設計的前提仍是基於需求, 因此咱們第一步是要確認需求. 一個進度條組件通常都會有以下需求點:node
需求收集好以後,做爲一個有追求的程序員, 會得出以下線框圖: react
這也是一個健壯的react/vue組件應有的思考角度.對於react選手來講,若是沒用typescript,我建議你們都用PropTypes, 它是react內置的類型檢測工具,咱們能夠直接在項目中導入. vue有自帶的屬性檢測方式,筆者在這一點上認爲vue仍是很貼心的.上面的思惟導圖咱們也知道了, 進度條組件的實現原理就是經過對外暴露必定的屬性,使用css先畫一個進度條, 最後經過屬性和樣式之間的調度來實現咱們需求滿滿的進度條.至於如何畫進度條,下面會詳細介紹.webpack
首先咱們會有一個容器來包裹咱們的進度條,進度條和進度提示文字分開(爲了更靈活的配置), 這樣咱們會獲得一個以下的html結構:css3
<div className={styles.progressWrap}>
<div className={styles.progressBar}>
<div className={styles.progressInnerBar}></div>
</div>
<span className={styles.progressText}>{percent + '%'}</span>
}
</div>
複製代碼
.progressBar用來作進度條背景, .progressInnerBar用來作實際的進度條, .progressText爲進度條文本.咱們經過控制.progressInnerBar的寬度就能實現進度條的變化了, css代碼以下:
.progressWrap {
margin: 6px 3px;
display: inline-flex;
align-items: center;
.progressBar {
position: relative;
display: inline-block;
height: 10px;
background-color: #f0f0f0;
border-radius: 5px;
overflow: hidden;
.progressInnerBar {
position: absolute;
height: 100%;
}
}
.progressText {
margin-left: 6px;
margin-top: -2px;
font-size: 14px;
}
}
複製代碼
沒錯,css代碼就這麼簡單, 咱們用了css3比較流行的額彈性佈局flex, css部分因爲都比較簡單,這裏我只提一點就是.progressInnerBar的css,使用絕對定位, 由於這個部分將來可能會作動畫,因此咱們把它作成離屏dom, 由於它只作展現,它的寬度徹底由js控制,後面咱們會將會看到.
咱們根據咱們收集到的需求, 能夠對外暴露7個自定義屬性(props),因此咱們的react組件必定是這樣的:
/** * 進度條組件 * @param {themeColor} string 進度條的顏色 * @param {percent} number 進度值百分比 * @param {autoHidden} boolean 是否進度到100%時自動消失 * @param {hiddenText} boolean 是否影藏進度條文本 * @param {width} string|number 進度條的寬度 * @param {textColor} string 進度文本顏色 * @param {statusScope} array 狀態閾值,分別設置不一樣進度範圍的進度條顏色,最大容許設置3個值, 爲一個二維數組 */
function Progress(props) {
let {
themeColor = '#06f',
percent = 0,
autoHidden = false,
hiddenText = false,
width = 320,
textColor = '#666',
statusScope
} = props
return
<div className={styles.progressWrap}> <div className={styles.progressBar} style={{ width: typeof width === 'number' ? width + 'px' : width }}> <div className={styles.progressInnerBar} style={{ width: `${percent}%` }} > </div> </div> { !hiddenText && <span className={styles.progressText} style={{ color: textColor }}>{percent + '%'}</span> } </div>
}
複製代碼
根據咱們收集到的額需求咱們很快能夠知道react組件須要暴露哪些屬性,而不會形成多餘的屬性,這一點是很是好的設計方法, 核心思想就是基於需求設計.因此咱們當肯定需求以後,其實組件已經實現了.這一經驗一致應用於筆者不少實際項目中,也清晰的指引着我組件的最終實現.剩幾個關鍵點以下:
react組件中,一個屬性不必定要顯性的賦值才能正常工做,好比上面代碼中的hiddenText屬性, 若是咱們不設置false或者true, 那麼react會默認爲false, 若是隻寫了hiddenText屬性而不賦值, react會自動認爲它的值爲true.這是react的一個設計細節,但願你們能瞭解掌握. 設置進度區間這個需求是組件惟一比較複雜的地方(相對來講,實際項目中有更復雜的案例),對應的屬性爲statusScope, 它的值爲一個數組,之因此爲數組是爲了開發人員更容易理解和使用,它的值可能以下:
let scope = [[30, 'red'], [60, 'orange'], [80, 'blue']]
複製代碼
最大閾值爲3,意思就是用戶能夠設置4種不一樣的進度狀態.每個狀態用不一樣的顏色代替.因爲用戶能夠不是按照從小到大的順序寫數組的,因此爲了組件的可靠性和容錯性, 筆者專門寫了排序方法對用戶傳來的額二維數組進行排序.具體代碼邏輯以下:
// 升序排序
let sortArr = arr => arr.sort((a,b) => a[0] - b[0])
// 檢測值所對應的進度條顏色狀態
function checkStatus(scope, val, defaultColor) {
val = +val
// 從小到大排序
sortArr(scope)
if(scope.length === 1) {
return val < scope[0][0] ? scope[0][1] : defaultColor
}else if(scope.length === 2) {
return val < scope[0][0] ? scope[0][1]
: scope[0][0] < val && val < scope[1][0] ? scope[1][1]
: defaultColor
}else if(scope.length === 3) {
return val < scope[0][0] ? scope[0][1]
: scope[0][0] < val && val < scope[1][0] ? scope[1][1]
: scope[1][0] < val && val < scope[2][0] ? scope[2][1]
: defaultColor
}
}
複製代碼
筆者不認爲checkStatus是最優的計算閾值顏色的方法, 你們能夠用更優雅的方法實現它.該方法的做用就是經過傳入用戶配置的區間和當前的進度值,來獲得當前進度條的顏色.
進度爲100%時進度條自動消失的邏輯也很簡單,就是判斷有這個屬性,而且進度爲100時將組件卸載就行了,因此相對完整的代碼以下:
import styles from './index.less'
// 升序排序
let sortArr = arr => arr.sort((a,b) => a[0] - b[0])
// 檢測值所對應的進度條顏色狀態
function checkStatus(scope, val, defaultColor) {
val = +val
// 從小到大排序
sortArr(scope)
if(scope.length === 1) {
return val < scope[0][0] ? scope[0][1] : defaultColor
}else if(scope.length === 2) {
return val < scope[0][0] ? scope[0][1]
: scope[0][0] < val && val < scope[1][0] ? scope[1][1]
: defaultColor
}else if(scope.length === 3) {
return val < scope[0][0] ? scope[0][1]
: scope[0][0] < val && val < scope[1][0] ? scope[1][1]
: scope[1][0] < val && val < scope[2][0] ? scope[2][1]
: defaultColor
}
}
/** * 進度條組件 * @param {themeColor} string 進度條的顏色 * @param {percent} number 進度值百分比 * @param {autoHidden} boolean 是否進度到100%時自動消失 * @param {hiddenText} boolean 是否影藏進度條文本 * @param {width} string|number 進度條的寬度 * @param {textColor} string 進度文本顏色 * @param {statusScope} array 狀態閾值,分別設置不一樣進度範圍的進度條顏色,最大容許設置3個值, 爲一個二維數組 */
function Progress(props) {
let {
themeColor = '#06f',
percent = 0,
autoHidden = false,
hiddenText = false,
width = 320,
textColor = '#666',
statusScope
} = props
return +percent === 100 && autoHidden ?
null :
<div className={styles.progressWrap}> <div className={styles.progressBar} style={{ width: typeof width === 'number' ? width + 'px' : width }}> <div className={styles.progressInnerBar} style={{ width: `${percent}%`, backgroundColor: statusScope && statusScope.length ? checkStatus(statusScope, percent, themeColor) : themeColor }} > </div> </div> { !hiddenText && <span className={styles.progressText} style={{ color: textColor }}>{percent + '%'}</span> } </div>
}
複製代碼
你們也許以爲到這裏咱們的組件就作好了.其實爲了咱們組件可以健壯的執行,咱們用propType來對屬性進行檢測.關於react的propTypes的用法,咱們能夠去react官網自行學習,用法也很簡單, 一下代碼我也會作完善的額註釋. 下面看看咱們完整的效果演示:
完整代碼以下:
import PropTypes from 'prop-types'
import styles from './index.less'
// 升序排序
let sortArr = arr => arr.sort((a,b) => a[0] - b[0])
// 檢測值所對應的進度條顏色狀態
function checkStatus(scope, val, defaultColor) {
val = +val
// 從小到大排序
sortArr(scope)
if(scope.length === 1) {
return val < scope[0][0] ? scope[0][1] : defaultColor
}else if(scope.length === 2) {
return val < scope[0][0] ? scope[0][1]
: scope[0][0] < val && val < scope[1][0] ? scope[1][1]
: defaultColor
}else if(scope.length === 3) {
return val < scope[0][0] ? scope[0][1]
: scope[0][0] < val && val < scope[1][0] ? scope[1][1]
: scope[1][0] < val && val < scope[2][0] ? scope[2][1]
: defaultColor
}
}
/** * 進度條組件 * @param {themeColor} string 進度條的顏色 * @param {percent} number 進度值百分比 * @param {autoHidden} boolean 是否進度到100%時自動消失 * @param {hiddenText} boolean 是否影藏進度條文本 * @param {width} string|number 進度條的寬度 * @param {textColor} string 進度文本顏色 * @param {statusScope} array 狀態閾值,分別設置不一樣進度範圍的進度條顏色,最大容許設置3個值, 爲一個二維數組 */
function Progress(props) {
let {
themeColor = '#06f',
percent = 0,
autoHidden = false,
hiddenText = false,
width = 320,
textColor = '#666',
statusScope
} = props
return +percent === 100 && autoHidden ?
null :
<div className={styles.progressWrap}> <div className={styles.progressBar} style={{ width: typeof width === 'number' ? width + 'px' : width }}> <div className={styles.progressInnerBar} style={{ width: `${percent}%`, backgroundColor: statusScope && statusScope.length ? checkStatus(statusScope, percent, themeColor) : themeColor }} > </div> </div> { !hiddenText && <span className={styles.progressText} style={{ color: textColor }}>{percent + '%'}</span> } </div>
}
Progress.propTypes = {
themeColor: PropTypes.string,
percent: PropTypes.number,
autoHidden: PropTypes.bool,
textAlign: PropTypes.string,
hiddenText: PropTypes.bool,
width: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
statusScope: PropTypes.array
}
export default Progress
複製代碼
關於如何使用,我就不作過多說明了,這裏舉2個例子:
<Progress percent={percent} width={240} autoHidden />
<Progress percent={10} themeColor="#6699FF" statusScope={[[18, 'red'], [40, 'orange']]} />
複製代碼
若是想學習更多H5遊戲, webpack,node,gulp,css3,javascript,nodeJS,canvas數據可視化等前端知識和實戰,歡迎在公號《趣談前端》加入咱們一塊兒學習討論,共同探索前端的邊界。