fleetback-react-native-progress
Version:
Progress indicators and spinners for React Native using ReactART
212 lines (198 loc) • 5.53 kB
JavaScript
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Animated, StyleSheet, Text, View } from 'react-native';
import { Surface as ARTSurface } from '@react-native-community/art';
import Arc from './Shapes/Arc';
import withAnimation from './withAnimation';
const CIRCLE = Math.PI * 2;
const AnimatedSurface = Animated.createAnimatedComponent(ARTSurface);
const AnimatedArc = Animated.createAnimatedComponent(Arc);
const styles = StyleSheet.create({
container: {
backgroundColor: 'transparent',
overflow: 'hidden',
},
});
export class ProgressCircle extends Component {
static propTypes = {
animated: PropTypes.bool,
borderColor: PropTypes.string,
borderWidth: PropTypes.number,
color: PropTypes.string,
children: PropTypes.node,
direction: PropTypes.oneOf(['clockwise', 'counter-clockwise']),
fill: PropTypes.string,
formatText: PropTypes.func,
indeterminate: PropTypes.bool,
progress: PropTypes.oneOfType([
PropTypes.number,
PropTypes.instanceOf(Animated.Value),
]),
rotation: PropTypes.instanceOf(Animated.Value),
showsText: PropTypes.bool,
size: PropTypes.number,
style: PropTypes.any,
strokeCap: PropTypes.oneOf(['butt', 'square', 'round']),
textStyle: PropTypes.any,
thickness: PropTypes.number,
unfilledColor: PropTypes.string,
endAngle: PropTypes.number,
allowFontScaling: PropTypes.bool,
};
static defaultProps = {
borderWidth: 1,
color: 'rgba(0, 122, 255, 1)',
direction: 'clockwise',
formatText: progress => `${Math.round(progress * 100)}%`,
progress: 0,
showsText: false,
size: 40,
thickness: 3,
endAngle: 0.9,
allowFontScaling: true,
};
componentDidMount() {
if (this.props.animated) {
this.props.progress.addListener(event => {
if (this.props.showsText || event.value === 1) {
this.forceUpdate();
}
});
}
}
render() {
const {
animated,
borderColor,
borderWidth,
color,
children,
direction,
fill,
formatText,
indeterminate,
progress,
rotation,
showsText,
size,
style,
strokeCap,
textStyle,
thickness,
unfilledColor,
endAngle,
allowFontScaling,
...restProps
} = this.props;
const border = borderWidth || (indeterminate ? 1 : 0);
const radius = size / 2 - border;
const offset = {
top: border,
left: border,
};
const textOffset = border + thickness;
const textSize = size - textOffset * 2;
const Surface = rotation ? AnimatedSurface : ARTSurface;
const Shape = animated ? AnimatedArc : Arc;
// eslint-disable-next-line no-underscore-dangle
const progressValue = animated ? progress.__getValue() : progress;
const angle = animated
? Animated.multiply(progress, CIRCLE)
: progress * CIRCLE;
return (
<View style={[styles.container, style]} {...restProps}>
<Surface
width={size}
height={size}
style={{
transform: [
{
rotate:
indeterminate && rotation
? rotation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
})
: '0deg',
},
],
}}
>
{unfilledColor && progressValue !== 1 ? (
<Shape
fill={fill}
radius={radius}
offset={offset}
startAngle={angle}
endAngle={CIRCLE}
direction={direction}
stroke={unfilledColor}
strokeWidth={thickness}
/>
) : (
false
)}
{!indeterminate ? (
<Shape
fill={fill}
radius={radius}
offset={offset}
startAngle={0}
endAngle={angle}
direction={direction}
stroke={color}
strokeCap={strokeCap}
strokeWidth={thickness}
/>
) : (
false
)}
{border ? (
<Arc
radius={size / 2}
startAngle={0}
endAngle={(indeterminate ? endAngle * 2 : 2) * Math.PI}
stroke={borderColor || color}
strokeCap={strokeCap}
strokeWidth={border}
/>
) : (
false
)}
</Surface>
{!indeterminate && showsText ? (
<View
style={{
position: 'absolute',
left: textOffset,
top: textOffset,
width: textSize,
height: textSize,
borderRadius: textSize / 2,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text
style={[
{
color,
fontSize: textSize / 4.5,
fontWeight: '300',
},
textStyle,
]}
allowFontScaling={allowFontScaling}
>
{formatText(progressValue)}
</Text>
</View>
) : (
false
)}
{children}
</View>
);
}
}
export default withAnimation(ProgressCircle);