Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animating Svg component does not work on android #1857

Closed
iandev096 opened this issue Sep 5, 2022 · 20 comments
Closed

Animating Svg component does not work on android #1857

iandev096 opened this issue Sep 5, 2022 · 20 comments

Comments

@iandev096
Copy link

Description

I am working on a progress loader component. The animation works on ios, but it doesn't work on android. I have tried it on a couple of different emulators as well as as actual devices, the the animation just does not render
Screen Shot 2022-09-05 at 11 18 51 AM

Steps to reproduce

Below is the code for the whole component

import React, { useEffect } from "react";
import { Modal, StyleSheet, Text, View } from "react-native";
import Animated, {
  useAnimatedProps,
  useSharedValue,
  withRepeat,
  withTiming,
} from "react-native-reanimated";
import Svg, { Circle } from "react-native-svg";


const CIRCLE_LENGTH = 201; // 2 * PI * r
const RADIUS = CIRCLE_LENGTH / (2 * Math.PI); // C / (2 * PI)
const STROKE_LENGTH = 4;

const AnimatedCircle = Animated.createAnimatedComponent(Circle);

export default function App() {
  const progress = useSharedValue(0);

  useEffect(() => {
    progress.value = withRepeat(withTiming(1, { duration: 5000 }), 100, true);
  }, [progress]);

  const animatedProps = useAnimatedProps(() => ({
    strokeDashoffset: CIRCLE_LENGTH * (1 - progress.value),
    strokeDasharray: CIRCLE_LENGTH,
  }));
  const baseCircleProps = {
    cx: RADIUS + STROKE_LENGTH / 2,
    cy: RADIUS + STROKE_LENGTH / 2,
    r: RADIUS,
    strokeWidth: STROKE_LENGTH,
    stroke: "#EEEEEE",
    // strokeLinecap: "round",
  };
  const baseAnimatedCircleProps = {
    ...baseCircleProps,
    stroke: "#161925",
    transform: `rotate(${-90},${RADIUS + STROKE_LENGTH / 2},${
      RADIUS + STROKE_LENGTH / 2
    })`,
  };

  return (
    <Modal animationType="fade" transparent={true} visible={true}>
      <View style={styles.background}>
        <View style={styles.progressContainer}>
          <Svg>
            <Circle {...baseCircleProps} />
            <AnimatedCircle
              animatedProps={animatedProps}
              {...baseAnimatedCircleProps}
            />
          </Svg>
          <Text numberOfLines={1} style={styles.loadingText}>
            Signing in...
          </Text>
        </View>
      </View>
    </Modal>
  );
}

const styles = StyleSheet.create({
  background: {
    backgroundColor: "#FFFFFF",
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  progressContainer: {
    width: RADIUS * 2 + STROKE_LENGTH,
    height: RADIUS * 2 + STROKE_LENGTH,
  },
  loadingText: {
    lineHeight: 20,
    fontSize: 16,
    textAlign: "center",
    marginTop: 27,
    overflow: "visible",
  },
});

Snack or a link to a repository

https://snack.expo.dev/@ianyimiah/reanimated-circular-progress-loader

Reanimated version

2.9.1

React Native version

0.69.5

@WoLewicki
Copy link
Member

Did you check if it works on the newest version of the library (13.1.0)?

@iandev096
Copy link
Author

I used version 12.3.0 because that is compatible with the version of expo I am using. But I just tried the newest version 13.1.0 and the issue still persists.

@espenjanson
Copy link

espenjanson commented Sep 8, 2022

useAnimatedProps doesn't work (at all?) on Android with 13.x :/

I mentioned it here too: #1845 (comment)

@espenjanson
Copy link

Some issues with useAnimatedProps seem to have been resolved with 13.2.0, but transform still doesn't work.

@WoLewicki
Copy link
Member

@espenjanson what do you mean by transform not working? Can you give examples of usages of the prop that you would like to have working? It would be easier then to make proper fixes to handle all scenarios.

@espenjanson
Copy link

espenjanson commented Sep 20, 2022

Hi @WoLewicki, thanks for trying to fix this!

Here's an example of code that worked in 12.4.0 but now doesn't. When I say work, I mean that it doesn't crash - but it does nothing. No transformation happens:

const animatedProps = useAnimatedProps(() => {
    const pos = getPositionOnCircle(angle.value)

    const x2 = pos.x
    const y2 = pos.y

    const degrees = toDeg(angle.value) + 90

    return {
      // translating x & y did not work 12.4.0 <= 13.0.1 but works again as of 13.0.2
      x: x2,
      y: y2,
      // line below still doesn't work
      transform: [{ rotate: `${degrees}deg` }],
    }
  })

Same thing goes for

const animatedProps = useAnimatedProps(() => {
    let scale = 1
    let opacity = 0
    if (getSleepItemIsRightNow(timeIndicatorAngle.value, angle)) {
      opacity = 1
      scale = 1.25
    }
    return {
      // opacity works
      opacity: withTiming(opacity),
      // line below doesn't work
      transform: [{ scale: withTiming(scale) }],
    }
  })

I dug around a bit in this repo and found this in src/lib/extract/types.ts:

 transform?: ColumnMajorTransformMatrix | string | TransformObject;
 // | TransformsStyle['transform']; // not used since it causes type problems
}

@WoLewicki
Copy link
Member

@espenjanson can you provide a simple repo with this code with its all context? It will greatly reduce the time needed to work on it.

@Duduzera1997
Copy link

Same problem on iOS with react-content-loader library.

https://github.com/danilowoz/react-content-loader/blob/master/src/native/Svg.tsx

@maxoschepkov
Copy link

maxoschepkov commented Oct 12, 2022

@WoLewicki here is an example of animated component that doesn't work and it causes crash on Android with RN 0.69.5 and latest libs (works fine on iOS):

import React, { useRef, useEffect } from 'react';
import { Animated, Easing, Dimensions, View } from 'react-native';
import Svg, { Path } from 'react-native-svg';

const AnimatedSvg = Animated.createAnimatedComponent(Svg);

export function ProcessingIndicator() {
  const translateX = useRef(new Animated.Value(0)).current;
  const opacity = useRef(new Animated.Value(0)).current;
  const screenWidth = Dimensions.get('window').width;
  /**
   * Animates moving of arrow from left to right.
   */
  useEffect(() => {
    Animated.parallel([
      Animated.delay(200),
      Animated.loop(
        Animated.sequence([
          Animated.timing(translateX, {
            toValue: 0.5,
            duration: 1200,
            easing: Easing.cubic,
            useNativeDriver: true,
          }),
          Animated.delay(400),
          Animated.timing(translateX, {
            toValue: 1,
            duration: 800,
            easing: Easing.cubic,
            useNativeDriver: true,
          }),
        ])
      ),
      Animated.timing(opacity, {
        toValue: 1,
        duration: 1200,
        easing: Easing.cubic,
        useNativeDriver: true,
      }),
    ]).start();
  }, []);

  return (
      <Animated.View style={{ opacity: opacity }}>
        <AnimatedSvg
          width={199}
          height={114}
          viewBox="0 0 199 114"
          fill="none"
          style={{
            transform: [
              {
                translateX: translateX.interpolate({
                  inputRange: [0, 0.25, 0.5, 1],
                  outputRange: [-screenWidth, 20, -20, screenWidth],
                }),
              },
            ],
          }}
        >
          <Path fill="#00000" d="M0 27h120v57H0z" />
          <Path d="M117.027 28.05V0L199 56.71l-81.973 56.711v-85.37Z" fill="#00000" />
        </AnimatedSvg>
      </Animated.View>
  );
}

@espenjanson
Copy link

@WoLewicki my issue seems to have been resolved with 13.4.0! However, I'm still getting a type error if I'm not ts-ignoring:


Type 'Partial<{ x: number; y: number; transform: { rotate: string; }[]; }>' is not assignable to type 'Partial<AnimateProps<GProps>>'.
  Types of property 'transform' are incompatible.
    Type '{ rotate: string; }[] | undefined' is not assignable to type 'string | TransformObject | ColumnMajorTransformMatrix | AnimatedNode<string | TransformObject | ColumnMajorTransformMatrix | undefined> | undefined'.
      Type '{ rotate: string; }[]' is not assignable to type 'string | TransformObject | ColumnMajorTransformMatrix | AnimatedNode<string | TransformObject | ColumnMajorTransformMatrix | undefined> | undefined'.
        Type '{ rotate: string; }[]' is not assignable to type 'ColumnMajorTransformMatrix'.ts(2322)
react-native-reanimated.d.ts(165, 7): The expected type comes from property 'animatedProps' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<AnimateProps<GProps>, any, any>> & Readonly<...>'
(JSX attribute) animatedProps?: Partial<Animated.AnimateProps<GProps>> | undefined

@WoLewicki
Copy link
Member

@espenjanson can you check if applying #1895 fixes the types issue?

@WoLewicki
Copy link
Member

@maxoschepkov I pasted your example with #1895 merged and it did not crash my app. @iandev096 does it work on earlier versions of the lib? Seems like the native side expects an array with even number of elements on Android for strokeDasharray prop, so if you use react-native-reanimated, you must make sure the prop you are passing is of such type (on iOS it is better since RCTConvert handles properly passing the prop:

if ([json isKindOfClass:[NSNumber class]]) {
). So your code should look somehow like this:

  const animatedProps = useAnimatedProps(() => ({
    strokeDashoffset: CIRCLE_LENGTH * (1 - progress.value),
    strokeDasharray: [CIRCLE_LENGTH, CIRCLE_LENGTH]
  }));

Does it fix your issue?

@iM-GeeKy
Copy link

iM-GeeKy commented Nov 2, 2022

I'm posting here in the case this is an upstream issue, but I'm getting the error posted in the screenshot here and posted some findings here. Any insight would be appreciated.

@WoLewicki
Copy link
Member

@iM-GeeKy are you sure 13.5.0 does not resolve the issue with null passed on Android. It for sure won't work in Expo Go app since it has native code compiled already. Did you try on a simple bare react-native project?

@WoLewicki
Copy link
Member

The error from video should be resolved by adding this line: 8cf4068#diff-73bfcd6b9672fc9694913b10a232038470557fd02a8d688932f64cdc4dc26fbfR111. Can you check if it is present in your project files? It should be there in 13.5.0

@iM-GeeKy
Copy link

iM-GeeKy commented Nov 4, 2022

The error from video should be resolved by adding this line: 8cf4068#diff-73bfcd6b9672fc9694913b10a232038470557fd02a8d688932f64cdc4dc26fbfR111. Can you check if it is present in your project files? It should be there in 13.5.0

Upgrading to 13.5.0 did resolve the issue. I appreciate the quick response! 🙌🏻

@WoLewicki
Copy link
Member

I will close this issue then. Feel free to comment if something is wrong and we can always reopen it then.

@siarheipashkevich
Copy link

@iM-GeeKy what about incompatible warning from expo doctor ?

@iM-GeeKy
Copy link

iM-GeeKy commented Jan 8, 2023

@iM-GeeKy what about incompatible warning from expo doctor ?

I ended up using the patch described here. That way it patches the compatible version with Expo 47. Using 13.5.0 did work, but I didn't want to risk hitting any more issues from using an incompatible version.

@siarheipashkevich
Copy link

@iM-GeeKy thanks 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants