《精通react/vue組件設計》之快速實現一個可定製的進度條組件

前言

這篇文章是筆者寫組件設計的第四篇文章,之因此會寫組件設計相關的文章,是由於做爲一名前端優秀的前端工程師,面對各類繁瑣而重複的工做,咱們不該該循序漸進的去"辛勤勞動",而是要根據已有前端的開發經驗,總結出一套本身的高效開發的方法.做爲數據驅動的領導者react/vue等MVVM框架的出現,幫咱們減小了工做中大量的冗餘代碼, 一切皆組件的思想深得人心.因此, 爲了讓工程師們有更多的時間去考慮業務和產品迭代,咱們不得不掌握高質量組件設計的思路和方法.因此筆者將花時間去總結各類業務場景下的組件的設計思路和方法,並用原生框架的語法去實現各類經常使用組件的開發,但願等讓前端新手或者有必定工做經驗的朋友能有所收穫.javascript

今天要來實現一個高可定製的進度條組件,在介紹組件設計以前,咱們先牢記如下幾個原則.css

每日一學: 組件設計三原則html

  • 高內聚, 低耦合(尤爲是vue/react組件中, 下降組件之間的耦合尤其重要)
  • 組件邊界劃分清晰(每個組件都有本身清晰的邊界劃分)
  • 單一職責(每個組件只負責某一特定的表現或者功能)

若是對於react/vue組件設計原理不熟悉的,能夠參考個人上一篇文章:前端

《精通react/vue組件設計》之用純css打造類materialUI的按鈕點擊動畫並封裝成react組件vue

正文

在開始組件設計以前但願你們對css3和js有必定的基礎.咱們先看看實現後的組件效果:java

上圖能夠知道封裝後的進度條組件經過對外暴露的接口(react/vue裏面能夠看作props屬性)能夠很快的實現多個不一樣的表現和重用.我將會使用react帶你們實現這個進度條組件, 你們不用擔憂技術棧不同,由於react實現的組件能夠很快套用於vue項目中, 因此說底層原理很是重要.

1. 組件原理和設計思路

因爲組件設計的前提仍是基於需求, 因此咱們第一步是要確認需求. 一個進度條組件通常都會有以下需求點:node

  • 經過進度控制進度條長度
  • 進度條總長度能夠由用戶來控制
  • 隨時修改精度條的額顏色(來自於設計師或產品經理獨特而百變的審美)
  • 當進度爲100%時進度條能夠自動消失(可能的需求)
  • 進度提示文本(用戶想知道當前長度下的具體進度, 好比體溫計)
  • 對於不一樣的進度節點,須要有不一樣的進度條顏色(好比遊戲人物裏的血, 快沒血的時候爲紅色, 血滿的時候爲藍色)

需求收集好以後,做爲一個有追求的程序員, 會得出以下線框圖: react

這也是一個健壯的react/vue組件應有的思考角度.對於react選手來講,若是沒用typescript,我建議你們都用PropTypes, 它是react內置的類型檢測工具,咱們能夠直接在項目中導入. vue有自帶的屬性檢測方式,筆者在這一點上認爲vue仍是很貼心的.

上面的思惟導圖咱們也知道了, 進度條組件的實現原理就是經過對外暴露必定的屬性,使用css先畫一個進度條, 最後經過屬性和樣式之間的調度來實現咱們需求滿滿的進度條.至於如何畫進度條,下面會詳細介紹.webpack

2. 基於react實現一個可定製的進度條組件

2.1. 實現進度條的靜態樣式

首先咱們會有一個容器來包裹咱們的進度條,進度條和進度提示文字分開(爲了更靈活的配置), 這樣咱們會獲得一個以下的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控制,後面咱們會將會看到.

2.2 實現組件外殼

咱們根據咱們收集到的需求, 能夠對外暴露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組件須要暴露哪些屬性,而不會形成多餘的屬性,這一點是很是好的設計方法, 核心思想就是基於需求設計.因此咱們當肯定需求以後,其實組件已經實現了.這一經驗一致應用於筆者不少實際項目中,也清晰的指引着我組件的最終實現.剩幾個關鍵點以下:

  • 設置進度區間
  • 進度爲100%時進度條自動消失

3. 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遊戲, webpacknodegulpcss3javascriptnodeJScanvas數據可視化等前端知識和實戰,歡迎在公號《趣談前端》加入咱們一塊兒學習討論,共同探索前端的邊界。

更多推薦

相關文章
相關標籤/搜索