Animated react-native-svg Circle sometimes don't render or render with delay
Description
I'm doing animation of strokeDashoffset and transform rotate of react-native-svg Circle. Basically works well except sometimes the circle doesn't render at all or renders without animation after some delay. See attached video.
https://user-images.githubusercontent.com/6594232/182143442-f2872af1-25fe-4996-b530-85e027f3fdb3.MP4
Snack or minimal code example
export const ACircle: FC<{
color: string;
circumference: number;
strokeDashoffset: number;
radius: number;
angle: number;
progress: Animated.SharedValue<number>;
center: number;
strokeWidth: number;
angleOffset: number;
}> = ({
color,
radius,
circumference,
strokeDashoffset,
angle,
progress,
center,
strokeWidth,
angleOffset,
}) => {
const animatedProps = useAnimatedProps(() => {
const _strokeDashoffset = interpolate(
progress.value,
[0, 1],
[circumference, strokeDashoffset],
Extrapolation.CLAMP,
);
const rotation = interpolate(
progress.value,
[0, 1],
[-angleOffset, angle],
Extrapolation.CLAMP,
);
return {
strokeDashoffset: _strokeDashoffset,
transform: [
{translateX: center},
{translateY: center},
{rotate: `${rotation}deg`},
{translateY: -center},
{translateX: -center},
],
};
}, [progress.value, angle, strokeDashoffset]);
return (
<AnimatedCircle
cx="50%"
cy="50%"
r={radius}
stroke={color}
fill="transparent"
strokeWidth={strokeWidth}
strokeDasharray={circumference}
// @ts-ignore
animatedProps={animatedProps}
/>
);
};
export const PieChart: FC<{
categories: SpendingCategoriesData;
size?: number;
strokeWidth?: number;
viewSize?: number;
angleOffset?: number;
}> = ({
categories,
size = 140,
strokeWidth = 40,
viewSize = 120,
angleOffset = 90,
}) => {
const radius = size / 2;
const boxSize = size + strokeWidth;
const circleCircumference = 2 * Math.PI * radius;
const center = boxSize / 2;
const progress = useSharedValue(0);
useEffect(() => {
progress.value = withTiming(1, {
duration: 600,
easing: Easing.inOut(Easing.ease),
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const measurements = useMemo(() => {
let angle = 0;
return categories.map(({percentage}) => {
const result = {
strokeDashoffset: circleCircumference * (1 - Number(percentage) / 100),
rotation: angle - angleOffset,
};
angle += (360 * Number(percentage)) / 100;
return result;
});
}, [categories, circleCircumference, angleOffset]);
return (
<View style={tw`w-${viewSize}px h-${viewSize}px`}>
<Svg
height={viewSize}
width={viewSize}
viewBox={`0 0 ${boxSize} ${boxSize}`}>
{categories.map((category, index) => (
<ACircle
key={`cirlecesection${index}`}
center={center}
radius={radius}
circumference={circleCircumference}
strokeDashoffset={measurements[index].strokeDashoffset}
angle={measurements[index].rotation}
progress={progress}
color={category.color}
strokeWidth={strokeWidth}
angleOffset={angleOffset}
/>
))}
</Svg>
</View>
);
}
Package versions
| name | version |
|---|---|
| react-native | 0.68.2 |
| react-native-reanimated | 2.8.0 |
| NodeJS | v16.15.1 |
| Xcode | 13.4.1 |
| expo | ^45.0.6 |
Affected platforms
- [ ] Android
- [x] iOS
- [ ] Web
Hello @dimaportenko! Thanks for reporting the issue. Can you please share with us a full minimal viable reproduction, preferably as a Expo Snack or a link to GitHub repository?
yep, I'll try to replicate it in a new project and post here somewhere this week.
hi @tomekzaw here is expo snack.
https://user-images.githubusercontent.com/6594232/183245323-e396a52f-273f-441c-9eff-f42e702b5458.MP4
Hey @dimaportenko! Thanks for preparing the repro. We will look into this issue early next week.
@dimaportenko Based on the Snack you have provided, I was able to successfully reproduce the issue. Moreover, it seems to appear more often if there is more circle sections in the pie chart.
App.tsx
+import * as React from 'react';
data.ts
const N = 100;
export const testData = [...Array(N)].map((_, i) => ({
value: i,
color: `rgb(0, ${Math.round(i * 255 / N)}, 255)`,
percentage: 100 / N,
}));
| Setup | Issue reproduced |
|---|---|
| Expo app SDK 45 (2.8.0) | ✔️ |
| Example app (2.8.0) | ✔️ |
| Example app (2.9.0) | ✔️ |
| Example app (2.9.1) | ✔️ |
| Example app (3.0.0-rc.0) | ✔️ |
| Example app (main) | ✖️ |
| Example app (branch Reanimated2 a8b939e195b4e7b94c7a2581f2a80926cf15b9c1) | ✖️ |
https://user-images.githubusercontent.com/20516055/183893079-e0861068-ddea-4d92-aef2-d0079870ed62.mov
https://user-images.githubusercontent.com/20516055/183893100-3c2ece08-dedf-4247-a3f0-526b3c85f0ae.mov
It looks like the issue might be already fixed by a PR merged some time after the release of 2.9.1 and 3.0.0-rc.0.
I will check whether the problem appears on 2.10 which we plan to release this week.
Thanks, I appreciate your effort!
Indeed, upgrade to Reanimated 2.10 resolves the issue. 🥳
"react-native": "0.68.2",
"react-native-reanimated": "2.10.0",
"react-native-svg": "13.1.0"
https://user-images.githubusercontent.com/20516055/187959831-e6a5cafc-f144-4521-897f-a5bab7aa589d.mov
edit: The bad news is that the repro works differently on Android (the actual angle is N times smaller) and also sometimes crashes, probably on the C++ side. 😢