重構-環形進度條
阿新 • • 發佈:2020-12-08
動畫: 顏色變化、寬度增加 圓環實現: 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);
}