react-native-cocentric-progress-circle
Version:
A react native concentric progress circle package
173 lines (157 loc) • 4.82 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import { View, ViewPropTypes } from 'react-native';
import { Svg, Path, G } from 'react-native-svg';
export default class CircularProgress extends React.PureComponent {
polarToCartesian(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;
return {
x: centerX + radius * Math.cos(angleInRadians),
y: centerY + radius * Math.sin(angleInRadians),
};
}
circlePath(x, y, radius, startAngle, endAngle) {
var start = this.polarToCartesian(x, y, radius, endAngle * 0.9999);
var end = this.polarToCartesian(x, y, radius, startAngle);
var largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
var d = ['M', start.x, start.y, 'A', radius, radius, 0, largeArcFlag, 0, end.x, end.y];
return d.join(' ');
}
clampFill = fill => Math.min(100, Math.max(0, fill));
colorGetter(fillPercent){
if(fillPercent > 1 && fillPercent < 50){
return '#ff5167'
}
if(fillPercent >= 50 && fillPercent < 75){
return '#f5a623'
}
if(fillPercent >= 75){
return '#7ccd1d'
}
else{
return '#c9c8ca'
}
}
render() {
const {
size,
width,
backgroundWidth,
tintColor,
backgroundColor,
style,
rotation,
lineCap,
arcSweepAngle,
fill,
children,
childrenContainerStyle,
padding,
renderCap,
dashedBackground,
shouldUseDefaultRAG
} = this.props;
const maxWidthCircle = backgroundWidth ? Math.max(width, backgroundWidth) : width;
const sizeWithPadding = size / 2 + padding / 2;
const radius = size / 2 - maxWidthCircle / 2 - padding / 2;
const backgroundPath = this.circlePath(
sizeWithPadding,
sizeWithPadding,
radius,
0,
arcSweepAngle
);
const currentFillAngle = (arcSweepAngle * this.clampFill(fill)) / 100;
const circlePath = this.circlePath(
sizeWithPadding,
sizeWithPadding,
radius,
0,
currentFillAngle
);
const coordinate = this.polarToCartesian(
sizeWithPadding,
sizeWithPadding,
radius,
currentFillAngle
);
const cap = this.props.renderCap ? this.props.renderCap({ center: coordinate, fill }) : null;
const offset = size - maxWidthCircle * 2;
const localChildrenContainerStyle = {
...{
position: 'absolute',
left: maxWidthCircle + padding / 2,
top: maxWidthCircle + padding / 2,
width: offset,
height: offset,
borderRadius: offset / 2,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
},
...childrenContainerStyle,
}
const dashedBackgroundStyle = dashedBackground.gap > 0
? dashedBackground
: { width:0, gap:0 };
const strokeDasharray = dashedBackground.gap > 0 ?
Object.values(dashedBackgroundStyle)
.map(value => parseInt(value))
: null;
return (
<View style={style}>
<Svg width={size + padding} height={size + padding}>
<G rotation={rotation} originX={(size + padding) / 2} originY={(size + padding) / 2}>
{backgroundColor && (
<Path
d={backgroundPath}
stroke={backgroundColor}
strokeWidth={backgroundWidth || width}
strokeLinecap={lineCap}
strokeDasharray={strokeDasharray}
fill="transparent"
/>
)}
{fill > 0 && (
<Path
d={circlePath}
stroke={shouldUseDefaultRAG ? this.colorGetter(Math.floor(fill)) : tintColor}
strokeWidth={width}
strokeLinecap={lineCap}
fill="transparent"
/>
)}
{cap}
</G>
</Svg>
{children && <View style={localChildrenContainerStyle}>{children(fill)}</View>}
</View>
);
}
}
CircularProgress.propTypes = {
style: ViewPropTypes.style,
size: PropTypes.number.isRequired,
fill: PropTypes.number.isRequired,
width: PropTypes.number.isRequired,
backgroundWidth: PropTypes.number,
tintColor: PropTypes.string,
backgroundColor: PropTypes.string,
rotation: PropTypes.number,
lineCap: PropTypes.string,
arcSweepAngle: PropTypes.number,
children: PropTypes.func,
childrenContainerStyle: ViewPropTypes.style,
padding: PropTypes.number,
renderCap: PropTypes.func,
dashedBackground: PropTypes.object,
shouldUseDefaultRAG: PropTypes.bool,
};
CircularProgress.defaultProps = {
tintColor: 'black',
rotation: 90,
lineCap: 'butt',
arcSweepAngle: 360,
padding: 0,
dashedBackground: { width: 0, gap: 0 },
};