
Vertical Step indicator
May 06, 2023
1 min
React Native component for creating animated, circular progress. Useful for displaying users points for example.
You can configure the CircularProgress-component by passing the following props:
| Name | Type | Default value | Description |
|---|---|---|---|
| size | number|Animated.Value | required | Width and height of circle |
| width | number | required | Thickness of the progress line |
| backgroundWidth | number | width | Thickness of background circle |
| fill | number (0-100) | 0 | Current progress / fill |
| tintColor | string | black | Color of the progress line |
| tintTransparency | boolean | true | Transparency of the progress line |
| backgroundColor | string | If unspecified, no background line will be rendered | |
| rotation | number (-360 - 360) | 90 | Angle from which the progress starts from |
| lineCap | string | butt | Shape used at ends of progress line. Possible values: butt, round, square |
| arcSweepAngle | number (0-360) | 360 | If you don’t want a full circle, specify the arc angle |
| style | ViewPropTypes.style | Extra styling for the main container | |
| children | function | Pass a function as a child. It received the current fill-value as an argument | |
| childrenContainerStyle | ViewPropTypes.style | Extra styling for the children container | |
| padding | number | 0 | Padding applied around the circle to allow for a cap that bleeds outside its boundary |
| dashedBackground | object | { width: 0, gap: 0 } | Bar background as dashed type |
| dashedTint | object | { width: 0, gap: 0 } | Bar tint as dashed type |
| renderCap | function | undefined | Function that’s invoked during rendering to draw at the tip of the progress circle |
The following props can further be used on AnimatedCircularProgress:
| Name | Type | Default value | Description |
|---|---|---|---|
| prefill | number (0-100) | 0 | Initial fill-value before animation starts |
| duration | number | 500 | Duration of animation in ms |
| delay | number | 0 | Delay of animation in ms |
| easing | function | Easing.out(Easing.ease) | Animation easing function |
| onAnimationComplete | function | Function that’s invoked when the animation completes (both on mount and if called with .animate()) | |
| onFillChange | function | Function that returns current progress on every change | |
| tintColorSecondary | string | the same as tintColor | To change fill color from tintColor to tintColorSecondary as animation progresses |
AnimatedCircularProgress also exposes the following functions:
| Name | Arguments | Description |
|---|---|---|
| animate | (toVal: number, duration: number, ease: function) | Animate the progress bar to a specific value |
| reAnimate | (prefill: number, toVal: number, duration: number, ease: function) | Re-run animation with a specified prefill-value |
Install this component and react-native-svg:
npm i --save react-native-circular-progress react-native-svg
Link native code for SVG:
react-native link react-native-svg
import React from 'react';
import { StyleSheet, Text, PanResponder, View, PanResponderInstance } from 'react-native';
import { AnimatedCircularProgress } from 'react-native-circular-progress';
const MAX_POINTS = 500;
export default class App extends React.Component {
state = {
isMoving: false,
pointsDelta: 0,
points: 325,
};
_panResponder : PanResponderInstance;
_circularProgressRef: React.RefObject<AnimatedCircularProgress>;
constructor(props: Readonly<{}>) {
super(props);
this._circularProgressRef = React.createRef();
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
this.setState({ isMoving: true, pointsDelta: 0 });
},
onPanResponderMove: (evt, gestureState) => {
if (this._circularProgressRef.current) {
this._circularProgressRef.current.animate(0, 0);
}
// For each 2 pixels add or subtract 1 point
this.setState({ pointsDelta: Math.round(-gestureState.dy / 2) });
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {
if (this._circularProgressRef.current) {
this._circularProgressRef.current.animate(100, 3000);
}
let points = this.state.points + this.state.pointsDelta;
console.log(Math.min(points, MAX_POINTS));
this.setState({
isMoving: false,
points: points > 0 ? Math.min(points, MAX_POINTS) : 0,
pointsDelta: 0,
});
},
});
}
render() {
const fill = (this.state.points / MAX_POINTS) * 100;
return (
<View style={styles.container} {...this._panResponder.panHandlers}>
<AnimatedCircularProgress
size={200}
width={3}
backgroundWidth={30}
fill={fill}
tintColor="#00e0ff"
backgroundColor="#3d5875"
>
{fill => <Text style={styles.points}>{Math.round((MAX_POINTS * fill) / 100)}</Text>}
</AnimatedCircularProgress>
<AnimatedCircularProgress
size={120}
width={15}
backgroundWidth={5}
fill={fill}
tintColor="#00ff00"
tintColorSecondary="#ff0000"
backgroundColor="#3d5875"
arcSweepAngle={240}
rotation={240}
lineCap="round"
/>
<AnimatedCircularProgress
size={100}
width={25}
fill={0}
tintColor="#00e0ff"
onAnimationComplete={() => console.log('onAnimationComplete')}
ref={this._circularProgressRef}
backgroundColor="#3d5875"
arcSweepAngle={180}
/>
<Text style={[styles.pointsDelta, this.state.isMoving && styles.pointsDeltaActive]}>
{this.state.pointsDelta >= 0 && '+'}
{this.state.pointsDelta}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
points: {
textAlign: 'center',
color: '#7591af',
fontSize: 50,
fontWeight: '100',
},
container: {
flex: 1,
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: '#152d44',
padding: 50,
},
pointsDelta: {
color: '#4c6479',
fontSize: 50,
fontWeight: '100',
},
pointsDeltaActive: {
color: '#fff',
},
});
Coming Soon…
Quick Links
Legal Stuff





