Created
March 25, 2025 14:36
-
-
Save farynaio/03a67db10b7dcecf1ed688b722918875 to your computer and use it in GitHub Desktop.
RN DonutChart component
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect, useState, useRef } from 'react' | |
import { Animated, StyleSheet, View, TextInput } from 'react-native' | |
import Svg, { G, Circle } from 'react-native-svg' | |
class CircleOmitter extends Circle { | |
public override render() { | |
const newProps = { | |
...this.props, | |
collapsable: undefined, | |
} | |
return <Circle {...newProps} /> | |
} | |
} | |
const AnimatedCircle = Animated.createAnimatedComponent(CircleOmitter) | |
const AnimatedInputText = Animated.createAnimatedComponent(TextInput) | |
export function DonutChart({ | |
percentage, | |
radius, | |
strokeWidth = 10, | |
duration = 500, | |
color = 'tomato', | |
delay = 500, | |
textColor = 'black', | |
max = 100, | |
label = null, | |
}) { | |
const animatedValue = useRef(new Animated.Value(0)).current | |
const halfCircle = radius + strokeWidth | |
const circleCircumference = 2 * Math.PI * radius | |
const [strokeDashoffset, setStrokeDashoffset] = useState(0) | |
const [inputTextVal, setInputTextVal] = useState('') | |
const animation = (toValue) => { | |
return Animated.timing(animatedValue, { | |
toValue, | |
duration, | |
delay, | |
useNativeDriver: true, | |
}).start() | |
// .start(() => { | |
// animation(toValue === 0 ? percentage : 0) | |
// }) | |
} | |
useEffect(() => { | |
animation(percentage) | |
animatedValue.addListener((v) => { | |
const maxPerc = (100 * v.value) / max | |
setStrokeDashoffset( | |
circleCircumference - (circleCircumference * maxPerc) / 100 | |
) | |
setInputTextVal(`${Math.round(v.value)}`) | |
}) | |
return () => { | |
animatedValue.removeAllListeners() | |
} | |
}, [animatedValue, circleCircumference, max, percentage]) | |
return ( | |
<View> | |
<Svg | |
width={radius * 2} | |
height={radius * 2} | |
viewBox={`0 0 ${halfCircle * 2} ${halfCircle * 2}`} | |
collapsable={undefined} | |
> | |
<G rotation="-90" origin={`${halfCircle}, ${halfCircle}`}> | |
<Circle | |
cx="50%" | |
cy="50%" | |
stroke="gray" | |
strokeWidth={strokeWidth} | |
r={radius} | |
strokeOpacity={0.2} | |
fill="transparent" | |
/> | |
<AnimatedCircle | |
cx="50%" | |
cy="50%" | |
stroke={color} | |
strokeWidth={strokeWidth} | |
r={radius} | |
fill="transparent" | |
strokeDasharray={circleCircumference} | |
strokeDashoffset={strokeDashoffset} | |
strokeLinecap="round" | |
/> | |
</G> | |
</Svg> | |
{/* <View style={[StyleSheet.absoluteFillObject]}> */} | |
<AnimatedInputText | |
underlineColorAndroid="transparent" | |
readOnly={true} | |
focusable={false} | |
value={inputTextVal} | |
style={[ | |
StyleSheet.absoluteFillObject, | |
{ fontSize: radius / 2, color: textColor ?? color }, | |
{ fontWeight: '900', textAlign: 'center' }, | |
]} | |
/> | |
{/* <View style={[StyleSheet.absoluteFillObject]}> */} | |
{label && ( | |
<TextInput | |
style={[ | |
StyleSheet.absoluteFillObject, | |
styles.label, | |
{ marginTop: radius - 10 }, | |
]} | |
readOnly={true} | |
focusable={false} | |
defaultValue={label} | |
/> | |
)} | |
{/* <Text style={[StyleSheet.absoluteFillObject, styles.label]}>{label}</Text> */} | |
{/* </View> */} | |
{/* </View> */} | |
</View> | |
) | |
} | |
const styles = StyleSheet.create({ | |
label: { | |
color: 'black', | |
textAlign: 'center', | |
// marginTop: 10, | |
fontSize: 14, | |
// margin: "auto", | |
// alignSelf: "center", | |
}, | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment