11import React , { forwardRef } from "react" ;
2- import { StyleSheet , TextInput , TextInputProps } from "react-native" ;
2+ import { StyleSheet } from "react-native" ;
33import Animated , {
44 Easing ,
55 interpolate ,
66 useAnimatedProps ,
7+ useAnimatedStyle ,
78 useDerivedValue ,
89 useSharedValue ,
910 withRepeat ,
@@ -12,16 +13,13 @@ import Animated, {
1213} from "react-native-reanimated" ;
1314import Svg , { Circle , G } from "react-native-svg" ;
1415
15- import { Box , BoxProps } from "../../primitives" ;
16+ import { AnimatedBox , Box , BoxProps , Text } from "../../primitives" ;
1617import { useTheme } from "../../theme" ;
17- import { createComponent , cx } from "../../utils" ;
18+ import { createComponent , cx , styleAdapter } from "../../utils" ;
1819
1920Animated . addWhitelistedNativeProps ( { text : true } ) ;
2021
2122const AnimatedCircle = Animated . createAnimatedComponent ( Circle ) ;
22- const AnimatedG = Animated . createAnimatedComponent ( G ) ;
23-
24- const AnimatedTextInput = Animated . createAnimatedComponent ( TextInput ) ;
2523
2624export type CircularProgressSizes = "sm" | "md" | "lg" | "xl" ;
2725export type CircularProgressTheme = "base" | "primary" ;
@@ -35,10 +33,6 @@ export interface CircularProgressProps extends BoxProps {
3533 * The size of the Circle
3634 */
3735 themeColor : CircularProgressTheme ;
38- /**
39- * Stroke Width of the Circle Border
40- */
41- strokeWidth : number ;
4236 /**
4337 * Color of Progress value
4438 */
@@ -64,10 +58,9 @@ export interface CircularProgressProps extends BoxProps {
6458 */
6559 max : number ;
6660 /**
67- * Should show progress value
68- * @default false
61+ * Hint for the Meter
6962 */
70- hint : boolean ;
63+ hint : string ;
7164}
7265
7366const SPRING_CONFIG = {
@@ -87,13 +80,13 @@ const RNCircularProgress: React.FC<Partial<CircularProgressProps>> = forwardRef<
8780 {
8881 size = "md" ,
8982 themeColor = "base" ,
90- strokeWidth = 2 ,
9183 progressTrackColor,
9284 trackColor,
9385 value,
9486 min = 0 ,
9587 max = 100 ,
9688 hint = false ,
89+ style,
9790 ...otherProps
9891 } ,
9992 ref ,
@@ -107,13 +100,10 @@ const RNCircularProgress: React.FC<Partial<CircularProgressProps>> = forwardRef<
107100 [ value ] ,
108101 ) ;
109102
103+ const strokeWidth = hint ? 5 : 10 ;
104+
110105 // Circle parameters
111- const radius = isIndeterminate
112- ? circularProgressTheme . size [ size ] ?. default
113- : hint
114- ? circularProgressTheme . size [ size ] ?. withHintSize
115- : circularProgressTheme . size [ size ] ?. default ;
116- const halfCircle = radius + strokeWidth ;
106+ const radius = 44 ;
117107 const circleCircumference = 2 * Math . PI * radius ;
118108
119109 // Animation for value based progress
@@ -137,40 +127,68 @@ const RNCircularProgress: React.FC<Partial<CircularProgressProps>> = forwardRef<
137127 } ) ;
138128
139129 // Indeterminate Progress
140- const progress = useSharedValue ( - 1 ) ;
141-
130+ const progress = useSharedValue ( 0 ) ;
131+ const rotate = useSharedValue ( 0 ) ;
132+ const animatedSvgStyle = useAnimatedStyle ( ( ) => {
133+ const rotateValue = interpolate ( rotate . value , [ 0 , 1 ] , [ 0 , 360 ] ) ;
134+ return {
135+ transform : [
136+ {
137+ rotate : `${ rotateValue } deg` ,
138+ } ,
139+ ] ,
140+ } ;
141+ } ) ;
142142 const indeterminateAnimatedCircularProgress = useAnimatedProps ( ( ) => {
143143 return {
144144 strokeDashoffset : interpolate (
145145 progress . value ,
146- [ - 1 , 1 ] ,
147- [ circleCircumference , - circleCircumference ] ,
146+ [ 0 , 0.5 , 1 ] ,
147+ [ 0 , - 276 , - ( 276 * 2 ) ] ,
148148 ) ,
149149 } ;
150150 } ) ;
151151
152152 React . useEffect ( ( ) => {
153153 progress . value = withRepeat (
154154 withTiming ( 1 , {
155- duration : 1750 ,
155+ duration : 1500 ,
156+ easing : Easing . linear ,
157+ } ) ,
158+ - 1 ,
159+ false ,
160+ ) ;
161+ rotate . value = withRepeat (
162+ withTiming ( 1 , {
163+ duration : 1000 ,
156164 easing : Easing . bezier ( 0.4 , 0 , 0.2 , 1 ) ,
157165 } ) ,
158166 - 1 ,
159167 false ,
160168 ) ;
161- } , [ progress ] ) ;
162- const animatedTextProps = useAnimatedProps ( ( ) => {
163- return { text : `${ progressValue . value } %` } as unknown as TextInputProps ;
164- } ) ;
169+ } , [ progress , rotate ] ) ;
170+
171+ const circularProgressBoxDimensions = {
172+ width : hint
173+ ? circularProgressTheme . size [ size ] ?. withHintSize
174+ : circularProgressTheme . size [ size ] ?. default ,
175+ height : hint
176+ ? circularProgressTheme . size [ size ] ?. withHintSize
177+ : circularProgressTheme . size [ size ] ?. default ,
178+ } ;
165179
166180 return (
167- < Box ref = { ref } { ...otherProps } >
168- < Svg
169- width = { radius * 2 }
170- height = { radius * 2 }
171- viewBox = { `0 0 ${ halfCircle * 2 } ${ halfCircle * 2 } ` }
172- >
173- < AnimatedG rotation = { "-90" } origin = { `${ halfCircle } , ${ halfCircle } ` } >
181+ < AnimatedBox
182+ ref = { ref }
183+ style = { [
184+ circularProgressBoxDimensions ,
185+ styleAdapter ( style , { pressed : false } , false ) ,
186+ isIndeterminate && animatedSvgStyle ,
187+ ] }
188+ { ...otherProps }
189+ >
190+ < Svg width = "100%" height = "100%" viewBox = { "0 0 100 100" } >
191+ < G rotation = { "-90" } origin = "50, 50" >
174192 < Circle
175193 stroke = {
176194 trackColor
@@ -185,8 +203,8 @@ const RNCircularProgress: React.FC<Partial<CircularProgressProps>> = forwardRef<
185203 strokeWidth = { strokeWidth }
186204 fill = "transparent"
187205 r = { radius }
188- cx = "50%"
189- cy = "50%"
206+ cx = { 50 }
207+ cy = { 50 }
190208 />
191209 { isIndeterminate && (
192210 < AnimatedCircle
@@ -203,10 +221,10 @@ const RNCircularProgress: React.FC<Partial<CircularProgressProps>> = forwardRef<
203221 strokeWidth = { strokeWidth }
204222 fill = "transparent"
205223 r = { radius }
206- cx = "50%"
207- cy = "50%"
224+ cx = { 50 }
225+ cy = { 50 }
208226 strokeLinecap = "round"
209- strokeDasharray = { ` ${ circleCircumference } ${ circleCircumference } ` }
227+ strokeDasharray = "276 276"
210228 animatedProps = { indeterminateAnimatedCircularProgress }
211229 />
212230 ) }
@@ -225,33 +243,37 @@ const RNCircularProgress: React.FC<Partial<CircularProgressProps>> = forwardRef<
225243 strokeWidth = { strokeWidth }
226244 fill = "transparent"
227245 r = { radius }
228- cx = "50%"
229- cy = "50%"
230- strokeDasharray = { circleCircumference }
246+ cx = { 50 }
247+ cy = { 50 }
231248 strokeLinecap = "round"
249+ strokeDasharray = { circleCircumference }
232250 animatedProps = { animatedCircleProps }
233251 />
234252 ) }
235- </ AnimatedG >
253+ </ G >
236254 </ Svg >
237255 { ! isIndeterminate && hint && (
238- < AnimatedTextInput
239- underlineColorAndroid = "transparent"
240- editable = { false }
241- defaultValue = { `${ progressValue . value } ` }
256+ < Box
242257 style = { [
243258 StyleSheet . absoluteFillObject ,
244- tailwind . style (
245- cx (
246- circularProgressTheme . text ,
247- circularProgressTheme . size [ size ] ?. text ,
248- ) ,
249- ) ,
259+ tailwind . style ( "justify-center items-center bg-transparent" ) ,
250260 ] }
251- animatedProps = { animatedTextProps }
252- />
261+ >
262+ < Text
263+ style = { [
264+ tailwind . style (
265+ cx (
266+ circularProgressTheme . text ,
267+ circularProgressTheme . size [ size ] ?. text ,
268+ ) ,
269+ ) ,
270+ ] }
271+ >
272+ { hint }
273+ </ Text >
274+ </ Box >
253275 ) }
254- </ Box >
276+ </ AnimatedBox >
255277 ) ;
256278 } ,
257279) ;
0 commit comments