1. 程式人生 > 其它 >重構-環形進度條

重構-環形進度條

技術標籤:手寫重構UI框架js

動畫:
	顏色變化、寬度增加

圓環實現:
	svg中兩個圓,背景色都為none,通過設定邊框為虛線,其中一個做背景,虛線的實線長度來實現圓環,實線還要設定stroke-linecap: round;形成圓角

引數:
  percentage: PropTypes.number,		數值,0-100
  onSuccess: PropTypes.func,		當100%時觸發,引數為狀態
  size: PropTypes.string,		    圓環大小,直徑(算邊框)
  strokeWidth: PropTypes.string,	圓環寬度
  colors: PropTypes.array,          進度條漸變顏色陣列,預設為color
  color: PropTypes.string,		    圓環顏色
  strokeColor:PropTypes.string,     進度條顏色
  fontSize: PropTypes.string,		字型大小
  fontColor:PropTypes.string,       字型顏色

效果圖:
在這裡插入圖片描述
在這裡插入圖片描述

程式碼示例:
使用:

import React,{useState,useRef,useEffect,useCallback} from 'react'
import Progress from './progress/progress'
import CircleProgress from './circleProgress/circleProgress'

function App(){
  let [value,setValue] =useState(0);
  const valf = useRef(null);
  valf.current = value;


  const
_onClick = function () { setInterval(() => { setValue(v=>v+1); },1000) } const _onSuccess = function (state) { console.log(state); } return( <div> <Progress colors={['#aaa', 'brown', 'blue', 'green']} percentage=
{99} onSuccess={_onSuccess} show ></Progress> <br/> <br /> <CircleProgress colors={['#aaa', 'brown', 'blue', 'green']} percentage={100} onSuccess={_onSuccess}></CircleProgress> <br/> <br /> <button onClick={_onClick}>開始</button> </div> ) } export default App

circleProgress.jsx:

import React,{useState,useEffect,useCallback,useMemo,useRef} from 'react'
import PropTypes from 'prop-types'
import './circleProgress.css'

function App(props){

  const circleProgressf = useRef(null);

  useEffect(() => {

    
    _styleHandler('size', props.size);
    _styleHandler('strokeWidth', props.strokeWidth);
    _styleHandler('color', props.color);
    _styleHandler('strokeColor', props.strokeColor);
    _styleHandler('fontSize', props.fontSize);
    _styleHandler('fontColor', props.fontColor);

  },[])

  useEffect(() => {
    //設定進度條
    circleProgressf.current.style.setProperty('--width', _percentageHandler * _oneOfpercentage)
    
    _percentageHandler == 100 && props.onSuccess(true);
    
     
    (props.colors.length!=0) && _setColor(_percentageHandler)

  }, [props.percentage])

  //統一樣式處理
  const _styleHandler = useCallback((key, value) => {
    console.log(props.size)
    props[key] && circleProgressf.current.style.setProperty('--'+key, value)
    
  }, [])

  //傳入數值處理
  const _percentageHandler = useMemo(() => {

    
    return Math.floor(Math.max(0,Math.min(100,props.percentage)))

  }, [props.percentage])
  
  //將數值換算成圓環百分比,即1數值=npx;
  const _oneOfpercentage = useMemo(() => {
    let r = parseFloat(props.size.substring(props.size.length - 2, -1)) / 2
    let strokeWidth = parseFloat(props.strokeWidth.substring(props.strokeWidth.length - 2, -1)) / 2
    let res = r - strokeWidth;

    let onePercent=(2*Math.PI * res)/100;
    
    return onePercent;
  }, [props.size])
  
  //座標處理
  const _positionHandler = useMemo(() => {
    let num = parseInt(props.size.substring(props.size.length - 2, -1)) / 2
    
    return num;
  },[props.size])


  //顏色變化處理
  const _setColor = useCallback((index) => {
    let _index = Math.floor(100 / props.colors.length);
    _index = Math.floor(index / _index);
    (_index == props.colors.length) &&( _index = props.colors.length - 1);


    circleProgressf.current.style.setProperty('--strokeColor',props.colors[_index])
    
  },[props.colors])

  
  


  return(

    <div className='jf-circleProgress-container' ref={circleProgressf}>
      <svg xmlns="http://www.w3.org/2000/svg" width='100%' height='100%'>
        {/*
          圓心:容器寬度的一半
          r:容器寬度一半-圓邊框寬度 
          邊框長度:2*pi*r
          進度條:n*((pi*r^2)/100)
        */}
        <circle className='jf-circleProgress-outer'></circle>
        <circle className='jf-circleProgress-inner' transform={`rotate(-90, ${_positionHandler}, ${_positionHandler})`}></circle>
        <text className='jf-circleProgress-number' x={_positionHandler} y={_positionHandler} >{ _percentageHandler }%</text>
      </svg>
    </div>
  )
}

export default React.memo(App);

App.propTypes = {
  percentage: PropTypes.number,
  onSuccess: PropTypes.func,
  size: PropTypes.string,
  strokeWidth: PropTypes.string,
  colors: PropTypes.array,
  color: PropTypes.string,
  strokeColor:PropTypes.string,
  fontSize: PropTypes.string,
  fontColor:PropTypes.string,

}

App.defaultProps = {
  percentage: 0,
  size: '125px',
  strokeWidth:'7px',
  onSuccess: () => { },
  colors:[]
  
}

circleProgress.css:

.jf-circleProgress-container {
  display: inline-block;
  margin-left: 20px;

  width: var(--size);
  height: var(--size);

  --size: 125px;
  --strokeWidth: 7px;
  --color: #ebeef5;
  --strokeColor: #13ce66;
  --width: 0;
  --fontSize: 16px;
  --fontColor: black;
}
.jf-circleProgress-outer {
  cx: calc(var(--size) / 2);
  cy: calc(var(--size) / 2);
  r: calc((var(--size) / 2) - (var(--strokeWidth) / 2));
  fill: none;
  stroke: var(--color);
  stroke-width: var(--strokeWidth);
  stroke-linecap: round;
  stroke-dasharray: 10000, 0;
}

.jf-circleProgress-inner {
  cx: calc(var(--size) / 2);
  cy: calc(var(--size) / 2);
  r: calc((var(--size) / 2) - (var(--strokeWidth) / 2));
  fill: none;
  stroke: var(--strokeColor);
  stroke-linecap: round;
  stroke-width: var(--strokeWidth);
  stroke-dasharray: var(--width), 10000;
  transition: stroke-dasharray 0.6s ease, stroke 1s;
}

.jf-circleProgress-number {
  x: calc(var(--size) / 2);
  y: calc(var(--size) / 2);
  text-anchor: middle;
  font-size: var(--fontSize);
  fill: var(--fontColor);
}