Skip to content

Instantly share code, notes, and snippets.

@farynaio
Created March 25, 2025 14:36
Show Gist options
  • Save farynaio/03a67db10b7dcecf1ed688b722918875 to your computer and use it in GitHub Desktop.
Save farynaio/03a67db10b7dcecf1ed688b722918875 to your computer and use it in GitHub Desktop.
RN DonutChart component
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