preface

This article is the fourth article writing component design, the writer has written component design related articles, because as a front-end good front end engineer, in the face of all sorts of tedious and repetitive work, we should not be on track to “hard work”, but according to the existing front-end development experience, summed up a set of effective method for developing. The emergence of MVVM frameworks like React/VUE, as data-driven leaders, helped us reduce a lot of redundant code in our work, and the idea of everything component became popular. Therefore, in order to give engineers more time to think about business and product iterations, we have to master the ideas and methods of high-quality component design. Therefore, the author will spend time to summarize the design ideas and methods of components in various business scenarios, and use the syntax of the native framework to realize the development of various commonly used components, hoping that the front-end novice or friends with certain work experience can gain something.

Today we’re going to implement a highly customizable progress bar component. Before we go into component design, let’s keep the following principles in mind.

Three Principles of Component Design

  • High cohesion, low coupling (especially in VUE/React components, it is important to reduce coupling between components)
  • Clear boundaries for components (each component has its own clear boundaries)
  • Single responsibility (each component is responsible for a specific performance or function)

If you are not familiar with react/ Vue component design principles, please refer to my previous article:

Master React/Vue Component Design creates materialui-like button click animations with pure CSS and encapsulates react components

The body of the

Before starting component design, I hope you have a basic knowledge of CSS3 and JS. Let’s take a look at the component after implementation:

1. Component principles and design ideas

Since the premise of component design is still based on requirements, our first step is to validate the requirements. A progress bar component typically has the following requirements:

  • The progress bar length is controlled by progress
  • The total length of the progress bar can be controlled by the user
  • Modify the accuracy bar color at any time (from the designer or product manager’s unique and varied aesthetic)
  • Progress bar can disappear automatically when progress reaches 100% (possible requirement)
  • Progress prompt text (the user wants to know the specific progress under the current length, such as thermometer)
  • – Different progress bar colors are required for different progress nodes (for example, blood is red when running out of blood and blue when it is full)

Once the requirements have been gathered, as an aspiring programmer, the following wireframes emerge:

As we know from the mind map above, the implementation principle of the progress bar component is to use CSS to draw a progress bar by exposing certain properties to the outside, and finally achieve the progress bar full of our needs by scheduling between properties and styles. As for how to draw a progress bar, more on that below.

Implement a customizable progress bar component based on React

2.1. Implement the static style of the progress bar

First we’ll have a container to wrap our progress bar, separate the progress bar from the progress prompt text (for more flexible configuration), and we’ll get an HTML structure like this:

<div className={styles.progressWrap}>
  <div className={styles.progressBar}>
    <div className={styles.progressInnerBar}></div>
  </div>
  <span className={styles.progressText}>{percent + '%'}</span>
  }
</div>
Copy the code

.progressbar for the background of the progressBar,.progressinnerbar for the actual progressBar, and.progresstext for the progressBar text. We can change the width of the.Progressinnerbar by controlling the width of the.Progressinnerbar.

.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; }}Copy the code

Yes, the CSS code is as simple as that, we use flex, which is popular in CSS3. CSS is partly because it’s relatively simple, so I’ll just mention one thing here. ProgressInnerBar CSS, using absolute positioning, Because this part is going to be animated in the future, we’ll make it an off-screen DOM, because it’s just for presentation, and its width is completely controlled by JS, as we’ll see later.

2.2 Component shell Implementation

We can expose 7 custom properties (props) based on the requirements we collected, so our React component must look like this:

/** * Progress bar components * @param {themeColor} String Progress bar color * @param {percent} number Progress value percentage * @param {autoHidden} Boolean Whether or not progress to 100% disappear automatically * @ param {hiddenText} Boolean whether to hide the progress bar text * @ param {width} string | number progress bar width * @ param {textColor} string Progress Text Color * @param {statusScope} Array Status threshold. Set progress bar colors for different progress ranges. A maximum of three values can be set, which is a two-dimensional array */
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>
}
Copy the code

According to the collected requirements, we can soon know which attributes the React component needs to expose without causing superfluous attributes. This is a very good design method, and the core idea is to design based on requirements. So when we determine the requirements, the component is already implemented. This experience has been consistently applied to many of my actual projects and has clearly guided my eventual implementation of the components. The remaining key points are:

  • Setting the progress interval
  • When the progress reaches 100%, the progress bar automatically disappears

React component details and final implementation

In the React component, a property doesn’t have to be explicitly assigned to work, such as the hiddenText property in the code above. If we don’t set it to false or true, then react will default to false. If we only write the hiddenText property and don’t assign, then react will default to false. React automatically thinks it’s true. This is a design detail of React that I hope you can understand. The requirement to set the progress interval is the only complex part of the component (there are relatively complex cases in actual projects). The corresponding attribute is statusScope, and its value is an array. The array is designed to make it easier for developers to understand and use.

let scope = [[30.'red'], [60.'orange'], [80.'blue']]
Copy the code

The maximum threshold is 3, which means that the user can set four different progress states. I’ll do a different color for each of these states. Because the user can not write the array in order from small to large, so in order to the reliability of components and fault tolerance, the author wrote a special sorting method to the user sent the amount of two-dimensional array sort. The specific code logic is as follows:

// Sort in ascending order
let sortArr = arr= > arr.sort((a,b) = > a[0] - b[0])

// The color status of the progress bar corresponding to the detected value
function checkStatus(scope, val, defaultColor) {
  val = +val
  // Order from smallest to largest
  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
  }
}
Copy the code

I do not think checkStatus is the best way to calculate the threshold color, you can use more elegant methods to implement it. The purpose of this method is to obtain the color of the current progress bar by passing in the user-configured interval and the current progress value.

When the progress is 100%, the progress bar automatically disappears. The logic is simple, which is to judge that this property exists and uninstall the component when the progress is 100. Therefore, the relatively complete code is as follows:

import styles from './index.less'

// Sort in ascending order
let sortArr = arr= > arr.sort((a,b) = > a[0] - b[0])

// The color status of the progress bar corresponding to the detected value
function checkStatus(scope, val, defaultColor) {
  val = +val
  // Order from smallest to largest
  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
  }
}

/** * Progress bar components * @param {themeColor} String Progress bar color * @param {percent} number Progress value percentage * @param {autoHidden} Boolean Whether or not progress to 100% disappear automatically * @ param {hiddenText} Boolean whether to hide the progress bar text * @ param {width} string | number progress bar width * @ param {textColor} string Progress Text Color * @param {statusScope} Array Status threshold. Set progress bar colors for different progress ranges. A maximum of three values can be set, which is a two-dimensional array */
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>
}
Copy the code

Now, you might think that at this point our components are done. In fact, in order for our components to perform robutly, we use propType to check the properties. As for the usage of propTypes in React, we can go to the react official website to learn by ourselves. The usage is also very simple, and I will make perfect comments on the code. Check out our full effect demo below:

The complete code is as follows:

import PropTypes from 'prop-types'
import styles from './index.less'

// Sort in ascending order
let sortArr = arr= > arr.sort((a,b) = > a[0] - b[0])

// The color status of the progress bar corresponding to the detected value
function checkStatus(scope, val, defaultColor) {
  val = +val
  // Order from smallest to largest
  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
  }
}


/** * Progress bar components * @param {themeColor} String Progress bar color * @param {percent} number Progress value percentage * @param {autoHidden} Boolean Whether or not progress to 100% disappear automatically * @ param {hiddenText} Boolean whether to hide the progress bar text * @ param {width} string | number progress bar width * @ param {textColor} string Progress Text Color * @param {statusScope} Array Status threshold. Set progress bar colors for different progress ranges. A maximum of three values can be set, which is a two-dimensional array */
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
Copy the code

I won’t go into too much detail on how to use it, but here are two examples:

<Progress
    percent={percent}
    width={240}
    autoHidden
/>
  
<Progress
    percent={10}
    themeColor="#6699FF"
    statusScope={[[18, 'red'], [40, 'orange']]} / >
Copy the code

The last

If you want to learn more H5 games, Webpack, node, gulp, CSS3, javascript, nodeJS, Canvas data visualization and other front-end knowledge and practical, welcome to join us in the public number “Interesting Talk front end” to learn and discuss, and explore the boundary of the front end.

More recommended

  • 2 years of vUE project practical experience summary
  • Master React/Vue Component Design creates materialui-like button click animations with pure CSS and encapsulates react components
  • Javascript Design Patterns front-end Engineers Need to Know in 15 minutes (with detailed mind maps and source code)
  • In 2019, take a look at some of my top questions and advice for job seekers
  • Re-encapsulates a real-time preview jsoneditor component based on jsonEditor (React version)
  • A picture shows you how to play vue-Cli3 quickly
  • Vue Advanced Advanced series – Play with Vue and vuex in typescript
  • “Front-end combat summary” the use of pure CSS website skin and focus diagram switch animation
  • “Front-end combat summary” using CSS3 to achieve cool 3D rotation perspective
  • Add a loading progress bar to your site using pace. Js
  • The Application of design Pattern of “Summary of Front End Actual Combat” — Memorandum Pattern
  • “Front End Combat Summary” using postMessage to achieve pluggable cross-domain chatbot
  • “Front-end combat summary” of the variable promotion, function declaration promotion and variable scope detailed explanation
  • “Front-end combat summary” how to change the URL without refreshing the page