react-native-reanimated icon indicating copy to clipboard operation
react-native-reanimated copied to clipboard

Animated react-native-svg Circle sometimes don't render or render with delay

Open dimaportenko opened this issue 3 years ago • 6 comments

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

dimaportenko avatar Aug 01 '22 12:08 dimaportenko

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?

tomekzaw avatar Aug 02 '22 13:08 tomekzaw

yep, I'll try to replicate it in a new project and post here somewhere this week.

dimaportenko avatar Aug 02 '22 13:08 dimaportenko

hi @tomekzaw here is expo snack.

https://user-images.githubusercontent.com/6594232/183245323-e396a52f-273f-441c-9eff-f42e702b5458.MP4

dimaportenko avatar Aug 06 '22 10:08 dimaportenko

Hey @dimaportenko! Thanks for preparing the repro. We will look into this issue early next week.

tomekzaw avatar Aug 06 '22 11:08 tomekzaw

@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.

tomekzaw avatar Aug 10 '22 11:08 tomekzaw

Thanks, I appreciate your effort!

dimaportenko avatar Aug 10 '22 13:08 dimaportenko

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. 😢

tomekzaw avatar Sep 01 '22 15:09 tomekzaw