software-mansion / react-native-svg

SVG library for React Native, React Native Web, and plain React web projects.
MIT License
7.48k stars 1.13k forks source link

[Android] [Reanimated] crash with react-native-svg 13.0.0 #1845

Closed mlecoq closed 2 years ago

mlecoq commented 2 years ago

Bug

After update from react-native-svg 12.1.0 to 13.0.0 I have a crash on android with the following stack trace

java_vm_ext.cc:577] JNI DETECTED ERROR IN APPLICATION: JNI GetObjectRefType called with pending exception java.lang.RuntimeException: Exception in HostFunction: java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
java_vm_ext.cc:577] 
java_vm_ext.cc:577] [native code]
java_vm_ext.cc:577] /Users/mlecoq/issues/reactNativeSvg13/node_modules/react-native-reanimated/src/reanimated2/UpdateProps.ts (46:26):1:267
java_vm_ext.cc:577] forEach@[native code]
java_vm_ext.cc:577] _f@/Users/mlecoq/issues/reactNativeSvg13/node_modules/react-native-reanimated/src/reanimated2/UpdateProps.ts (46:26):1:229
java_vm_ext.cc:577] [native code]
java_vm_ext.cc:577] styleUpdater@/Users/mlecoq/issues/reactNativeSvg13/node_modules/react-native-reanimated/src/reanimated2/hook/useAnimatedStyle.ts (185:0):1:1810

Unexpected behavior

Crash occurs since 13.0.0

Environment info

Run react-native info in your terminal and copy the results here. Also, include the precise version number of this library that you are using in the project

React native info output:

System:
    OS: macOS 12.5.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 105.06 MB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.13.1 - /var/folders/md/1qkynwbs1216d57grkbc682h0000gn/T/fnm_multishells/19554_1661241167202/bin/node
    Yarn: 1.22.15 - /var/folders/md/1qkynwbs1216d57grkbc682h0000gn/T/fnm_multishells/19554_1661241167202/bin/yarn
    npm: 8.1.2 - /var/folders/md/1qkynwbs1216d57grkbc682h0000gn/T/fnm_multishells/19554_1661241167202/bin/npm
    Watchman: 2022.03.21.00 - /opt/homebrew/bin/watchman
  Managers:
    CocoaPods: 1.11.3 - /usr/local/bin/pod
  SDKs:
    iOS SDK:
      Platforms: DriverKit 21.4, iOS 15.5, macOS 12.3, tvOS 15.4, watchOS 8.5
    Android SDK: Not Found
  IDEs:
    Android Studio: 2020.3 AI-203.7717.56.2031.7935034
    Xcode: 13.4.1/13F100 - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.13 - /usr/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.0.0 => 18.0.0 
    react-native: 0.69.4 => 0.69.4 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Library version: 13.0.0

Steps To Reproduce

Issues without reproduction steps or code are likely to stall.

  1. git clone https://github.com/mlecoq/reactNativeSvg13
  2. cd reactNativeSvg13
  3. yarn
  4. yarn android

The project crashes at startup (no crash with 12.1.0)

d-moreira commented 2 years ago

I'm also experiencing this issue with react-native-svg on v13.0.0 (after upgrading from v12.1.0). My setup:

System:
    OS: macOS 12.5.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 105.06 MB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.13.1 - /var/folders/md/1qkynwbs1216d57grkbc682h0000gn/T/fnm_multishells/19554_1661241167202/bin/node
    Yarn: 1.22.15 - /var/folders/md/1qkynwbs1216d57grkbc682h0000gn/T/fnm_multishells/19554_1661241167202/bin/yarn
    npm: 8.1.2 - /var/folders/md/1qkynwbs1216d57grkbc682h0000gn/T/fnm_multishells/19554_1661241167202/bin/npm
    Watchman: 2022.03.21.00 - /opt/homebrew/bin/watchman
  Managers:
    CocoaPods: 1.11.3 - /usr/local/bin/pod
  SDKs:
    iOS SDK:
      Platforms: DriverKit 21.4, iOS 15.5, macOS 12.3, tvOS 15.4, watchOS 8.5
    Android SDK: Not Found
  IDEs:
    Android Studio: 2020.3 AI-203.7717.56.2031.7935034
    Xcode: 13.4.1/13F100 - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.13 - /usr/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.0.0 => 18.0.0 
    react-native: 0.69.4 => 0.69.4 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found
eprice122 commented 2 years ago

I am experiencing a similar issue as well

WoLewicki commented 2 years ago

It is the effect of both Paper and Fabric implementations using delegates and interfaces added with Fabric integration. Due to problems with codegen, all props are now stringified in render methods, and color related props are parsed differently now too. Due to those changes, when using reanimated, you also have to stringify all props that were previously of type NumberProp, and parse props of color type acordingly. For your use-case, it would be something like this:

    return {
      cx: String(coordinates.cx),
      cy: String(coordinates.cx),
      rx: String(coordinates.rx),
      ry: String(coordinates.ry),
      stroke: {type: 0, value: processColor('rgb(0,0,0)')},
      opacity: 0,
      strokeWidth: String(0),
    };

Unfortunately there seems to be a bug in reanimated codebase since stroke prop cannot be parsed in my testing env. We will try to come up with a more generic solution for this soon hopefully.

mlecoq commented 2 years ago

@WoLewicki thanks for your explanations, yes I started first to stringify params but since my code was failing on stroke (which expects ReadableMap), I did not dig any further.

evelant commented 2 years ago

Seeing what I think is the same issue here upon trying to upgrade to 13.0.0. java.lang.Double cannot be cast to com.facebook.react.bridge.ReadableMap at setProperty RNSVGTextManagerDelegate.java

TomCorvus commented 2 years ago

Hi there, I add this here. I have no animation with react-content-loader since react-native-svg 13.0.0 only on Android. Everything works fine on iOS. And I have a fatal crash with this error:

Fatal Exception: java.lang.NullPointerException
Attempt to invoke virtual method 'java.lang.String java.lang.String.trim()' on a null object reference
com.horcrux.svg.SVGLength.<init> (SVGLength.java:37)

This library doesn't use reanimated but seems to use animation from react-native.

Here an content-loader example:

<ContentLoader
   viewBox="0 0 360 100"
   height={100}
   width={360}
   backgroundColor={'#333'}
   foregroundColor="#999">
      <Circle x="70" y="20" cx="20" cy="20" r="20" />
      <Rect x="110" y="38" rx="0" ry="0" width="20" height="4" />
      <Circle x="130" y="20" cx="20" cy="20" r="20" />
      <Rect x="170" y="38" rx="0" ry="0" width="20" height="4" />
      <Circle x="190" y="20" cx="20" cy="20" r="20" />
      <Rect x="230" y="38" rx="0" ry="0" width="20" height="4" />
      <Circle x="250" y="20" cx="20" cy="20" r="20" />
</ContentLoader>
AlirezaHadjar commented 2 years ago

Is this bug fixed in 13.1.0? @WoLewicki

WoLewicki commented 2 years ago

Unfortunately I don't think it is. Since react-native-reanimated updates the props directly and does not go through render method, the typings may differ there leading to a crash 😞

Tohix commented 2 years ago

Guys, If i want to use fill property via useAnimatedProps. Example:

const animatedProps = useAnimatedProps(() => {
    return {
      fill: "#cccccc",
    };
  }

  <AnimatedEllipse animatedProps={animatedProps} />

In version 12.3.0 it is working, on 13+ i have similar error. Or i do something incorrect?

espenjanson commented 2 years ago

Unfortunately super weird behavior on Android with Reanimated, useAnimatedProps barely seems to work anymore :/

This code worked well in 12.4.0, but does not work now (no crash, but it does nothing):

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

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

    const degrees = toDeg(angle.value) + 90

    return {
      x: x2,
      y: y2,
      transform: [{ rotate: `${degrees}deg` }],
    }
  })
maxoschepkov commented 2 years ago

Hi there, I add this here. I have no animation with react-content-loader since react-native-svg 13.0.0 only on Android. Everything works fine on iOS. And I have a fatal crash with this error:

Fatal Exception: java.lang.NullPointerException
Attempt to invoke virtual method 'java.lang.String java.lang.String.trim()' on a null object reference
com.horcrux.svg.SVGLength.<init> (SVGLength.java:37)

This library doesn't use reanimated but seems to use animation from react-native.

Here an content-loader example:

<ContentLoader
   viewBox="0 0 360 100"
   height={100}
   width={360}
   backgroundColor={'#333'}
   foregroundColor="#999">
      <Circle x="70" y="20" cx="20" cy="20" r="20" />
      <Rect x="110" y="38" rx="0" ry="0" width="20" height="4" />
      <Circle x="130" y="20" cx="20" cy="20" r="20" />
      <Rect x="170" y="38" rx="0" ry="0" width="20" height="4" />
      <Circle x="190" y="20" cx="20" cy="20" r="20" />
      <Rect x="230" y="38" rx="0" ry="0" width="20" height="4" />
      <Circle x="250" y="20" cx="20" cy="20" r="20" />
</ContentLoader>

Have similar issue, when I try to animate SVG app crashes .

WoLewicki commented 2 years ago

Can you check if applying https://github.com/react-native-svg/react-native-svg/pull/1869 fixes your issue? Make sure to correctly treat fill and stroke props. Example of it can be found in the test case: https://github.com/react-native-svg/react-native-svg/pull/1869/files#diff-76a76277daf14518270e8aea8a5e9358a8215d7e4276d2e5f1c4fe95107cdc20

mlecoq commented 2 years ago

@WoLewicki I have tried your PR on the following example https://github.com/mlecoq/reactNativeSvg13 and it still crashes

java_vm_ext.cc:578] JNI DETECTED ERROR IN APPLICATION: JNI GetObjectRefType called with pending exception java.lang.RuntimeException: Exception in HostFunction: java.lang.ClassCastException: java.lang.String cannot be cast to com.facebook.react.bridge.ReadableMap
java_vm_ext.cc:578] 
java_vm_ext.cc:578] [native code]
java_vm_ext.cc:578] 
mlecoq commented 2 years ago

I have tried to replace

 stroke: 'rgb(0,0,0)',

with

  stroke: {type: 0, value: processColor('rgb(0,0,0)')},

and I have the following error

 Error: invalid value passed to `stroke`, maybe you forgot to use `.value`?
WoLewicki commented 2 years ago

As pointed in the https://github.com/react-native-svg/react-native-svg/issues/1845#issuecomment-1247009705, the best way to handle fill and stroke props in by using createAnimatedPropAdapter which was made for this particular case, where props on the native side have different structure than in JS. Also, I changed the value prop to payload so it does not confuse reanimated babel plugin. Your animatedProps should look e.g. like this:

const ellipseAnimatedProps = 
   useAnimatedProps(() => 
   {
     const coordinates = {cx: 50, cy: 50, rx: 40, ry: 40};

     return {
       cx: coordinates.cx,
       cy: coordinates.cy,
       rx: coordinates.rx,
       ry: coordinates.ry,
       stroke: 'rgb(255,0,0)',
       fill: 'yellow',
       opacity: offset.value,
       strokeWidth: 2,
     };
   }
   , [], createAnimatedPropAdapter(
    (props) => {
      if (Object.keys(props).includes('fill')) {
        props.fill = {type: 0, payload: processColor(props.fill)}
      }
      if (Object.keys(props).includes('stroke')) {
        props.stroke = {type: 0, payload: processColor(props.stroke)}
      }
    },
    ['fill', 'stroke']));
mlecoq commented 2 years ago

Yes I just saw your example with the adapter, it works fine now, thanks a lot !

WoLewicki commented 2 years ago

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

buschco commented 1 year ago

I am just wondering how to fix this error using react-natives Animated API?

Here is an Example which is not working on the current main:

java.lang.String cannot be cast to com.facebook.react.bridge.ReadableMap
  const animation = React.useRef(new Animated.Value(0));

  React.useEffect(() => {
    Animated.timing(animation.current, {
      toValue: 1,
      duration: 300,
      delay: 100,
      easing: Easing.out(Easing.quad),
      useNativeDriver: false,
    }).start();
  }, []);

  return (
    <SafeAreaView>
      <Svg width={40} height={40} viewBox="0 0 40 40" fill="none">
        <AnimatedPath
          fill={animation.current.interpolate({
            inputRange: [0, 0.4, 1],
            outputRange: ['#1F1F34', '#FFD761', '#FA9502'],
          })}
          d="M16.0489 0.927051C16.3483 0.00574017 17.6517 0.00574017 17.9511 0.927051L21.2658 11.1287C21.3996 11.5407 21.7836 11.8197 22.2168 11.8197H32.9434C33.9122 11.8197 34.3149 13.0593 33.5312 13.6287L24.8532 19.9336C24.5027 20.1883 24.3561 20.6396 24.4899 21.0517L27.8046 31.2533C28.104 32.1746 27.0495 32.9407 26.2658 32.3713L17.5878 26.0664C17.2373 25.8117 16.7627 25.8117 16.4122 26.0664L7.73419 32.3713C6.95048 32.9407 5.896 32.1746 6.19535 31.2533L9.51006 21.0517C9.64393 20.6396 9.49728 20.1883 9.14679 19.9336L0.468768 13.6287C-0.314945 13.0593 0.0878303 11.8197 1.05655 11.8197H11.7832C12.2164 11.8197 12.6004 11.5407 12.7342 11.1287L16.0489 0.927051Z"
        />
      </Svg>
    </SafeAreaView>
  );

https://github.com/buschco/react-native-svg/commit/a1cbc203ddf6065c484b3d15bc7cf5e06f4ed607#diff-3ad04430c13b851d216fa0f686b4f9e4a7d013b50d492d9b27d969ccef669b74

evelant commented 1 year ago

Turns out my java.lang.Double cannot be cast to com.facebook.react.bridge.ReadableMap was just a misleading error message. Double check the props you're passing to <SVG -- I was accidentally passing an empty string "" for the fill prop on an svg text element. For some reason that got converted to a java.lang.Double resulting in the confusing error message.

WoLewicki commented 1 year ago

I am just wondering how to fix this error using react-natives Animated API?

You need to comply to the format of fill prop that is passed to the native side (since the props are not going through render and are not parsed by the lib), which is defined e.g. here: https://github.com/software-mansion/react-native-svg/blob/9c0fa78d72c9e32644b10f55090ae29edd1f5856/src/fabric/PathNativeComponent.ts#L25

Can you try if applying this commit: 4d16dc4 (#1914) fixes the problem?

buschco commented 1 year ago

@WoLewicki thank you it works like a charm 😍

Lieutenant-Vernyc commented 1 year ago

Turns out my java.lang.Double cannot be cast to com.facebook.react.bridge.ReadableMap was just a misleading error message. Double check the props you're passing to <SVG -- I was accidentally passing an empty string "" for the fill prop on an svg text element. For some reason that got converted to a java.lang.Double resulting in the confusing error message.

You have certainly saved me days of headaches!

the-kenneth commented 1 year ago

I am just wondering how to fix this error using react-natives Animated API?

You need to comply to the format of fill prop that is passed to the native side (since the props are not going through render and are not parsed by the lib), which is defined e.g. here:

https://github.com/software-mansion/react-native-svg/blob/9c0fa78d72c9e32644b10f55090ae29edd1f5856/src/fabric/PathNativeComponent.ts#L25

Can you try if applying this commit: 4d16dc4 (#1914) fixes the problem?

Can a similar fix be put in place for the stroke prop? Or is there a future plan to fix this properly?

XantreDev commented 1 year ago

Can we add this solution to the docs?

As pointed in the #1845 (comment), the best way to handle fill and stroke props in by using createAnimatedPropAdapter which was made for this particular case, where props on the native side have different structure than in JS. Also, I changed the value prop to payload so it does not confuse reanimated babel plugin. Your animatedProps should look e.g. like this:

const ellipseAnimatedProps = 
   useAnimatedProps(() => 
   {
     const coordinates = {cx: 50, cy: 50, rx: 40, ry: 40};

     return {
       cx: coordinates.cx,
       cy: coordinates.cy,
       rx: coordinates.rx,
       ry: coordinates.ry,
       stroke: 'rgb(255,0,0)',
       fill: 'yellow',
       opacity: offset.value,
       strokeWidth: 2,
     };
   }
   , [], createAnimatedPropAdapter(
    (props) => {
      if (Object.keys(props).includes('fill')) {
        props.fill = {type: 0, payload: processColor(props.fill)}
      }
      if (Object.keys(props).includes('stroke')) {
        props.stroke = {type: 0, payload: processColor(props.stroke)}
      }
    },
    ['fill', 'stroke']));
YouuuMeee commented 1 year ago

Bonjour, Je suis désolée, je ne sais pas exactement de quoi vous parler mais pas soucis évidemment et merci ^^

Envoyé à partir de Outlook pour iOShttps://aka.ms/o0ukef


De : Valery Smirnov @.> Envoyé : Monday, June 19, 2023 10:54:08 AM À : software-mansion/react-native-svg @.> Cc : Subscribed @.***> Objet : Re: [software-mansion/react-native-svg] [Android] [Reanimated] crash with react-native-svg 13.0.0 (Issue #1845)

Can we add this solution to the docs?

As pointed in the #1845 (comment)https://github.com/software-mansion/react-native-svg/issues/1845#issuecomment-1247009705, the best way to handle fill and stroke props in by using createAnimatedPropAdapter which was made for this particular case, where props on the native side have different structure than in JS. Also, I changed the value prop to payload so it does not confuse reanimated babel plugin. Your animatedProps should look e.g. like this:

const ellipseAnimatedProps = useAnimatedProps(() => { const coordinates = {cx: 50, cy: 50, rx: 40, ry: 40};

 return {
   cx: coordinates.cx,
   cy: coordinates.cy,
   rx: coordinates.rx,
   ry: coordinates.ry,
   stroke: 'rgb(255,0,0)',
   fill: 'yellow',
   opacity: offset.value,
   strokeWidth: 2,
 };

} , [], createAnimatedPropAdapter( (props) => { if (Object.keys(props).includes('fill')) { props.fill = {type: 0, payload: processColor(props.fill)} } if (Object.keys(props).includes('stroke')) { props.stroke = {type: 0, payload: processColor(props.stroke)} } }, ['fill', 'stroke']));

— Reply to this email directly, view it on GitHubhttps://github.com/software-mansion/react-native-svg/issues/1845#issuecomment-1596779135, or unsubscribehttps://github.com/notifications/unsubscribe-auth/A7NVM2QTUYESKXSBXVH7LZLXMAHTBANCNFSM57KW7YWQ. You are receiving this because you are subscribed to this thread.Message ID: @.***>

Nandha-d3v commented 1 year ago

I'm still facing the issue on Android, Animation starts asusual but after a while I see an error on Device but it doesnt appear on console

image

Here is how it looks on device

image

Can you help @WoLewicki

WoLewicki commented 1 year ago

Are you sure you are not changing the type of the prop? Since getType method fails, it seems that it gets a different type than expected, maybe null? It's hard to tell anything more based on screenshots only :(

thanhthanchuc commented 1 year ago

Turns out my java.lang.Double cannot be cast to com.facebook.react.bridge.ReadableMap was just a misleading error message. Double check the props you're passing to <SVG -- I was accidentally passing an empty string "" for the fill prop on an svg text element. For some reason that got converted to a java.lang.Double resulting in the confusing error message.

I took me a whole afternoon to figure out wtf was going on and it turned out, there was a stupid fill property has an empty string =.=

quicksilverr commented 1 year ago

Hey @WoLewicki

How will we do it in this case?

const animation = useAnimatedProps(() => {
      const fill = interpolateColor(
        progress.value,
        [0, 1],
        [uncheckedBackgroundColor, checkedBackgroundColor]
      );
      const stroke = interpolateColor(
        progress.value,
        [0, 1],
        [uncheckedBorderColor, checkedBorderColor]
      );
      return { fill, stroke };
    });
oiver555 commented 11 months ago
const ellipseAnimatedProps = 
   useAnimatedProps(() => 
   {
     const coordinates = {cx: 50, cy: 50, rx: 40, ry: 40};

     return {
       cx: coordinates.cx,
       cy: coordinates.cy,
       rx: coordinates.rx,
       ry: coordinates.ry,
       stroke: 'rgb(255,0,0)',
       fill: 'yellow',
       opacity: offset.value,
       strokeWidth: 2,
     };
   }
   , [], createAnimatedPropAdapter(
    (props) => {
      if (Object.keys(props).includes('fill')) {
        props.fill = {type: 0, payload: processColor(props.fill)}
      }
      if (Object.keys(props).includes('stroke')) {
        props.stroke = {type: 0, payload: processColor(props.stroke)}
      }
    },

Wow a hidden gem! I was about to say that this solution should be put on the docs, but I checked and it's already there :). You know when you have this issue you don't really know inherently to look into the "createAnimatedPropAdapter" to get the solution but I'm glad I found it either way!

MarlonAEC commented 10 months ago

I am just wondering how to fix this error using react-natives Animated API?

Here is an Example which is not working on the current main:

java.lang.String cannot be cast to com.facebook.react.bridge.ReadableMap
  const animation = React.useRef(new Animated.Value(0));

  React.useEffect(() => {
    Animated.timing(animation.current, {
      toValue: 1,
      duration: 300,
      delay: 100,
      easing: Easing.out(Easing.quad),
      useNativeDriver: false,
    }).start();
  }, []);

  return (
    <SafeAreaView>
      <Svg width={40} height={40} viewBox="0 0 40 40" fill="none">
        <AnimatedPath
          fill={animation.current.interpolate({
            inputRange: [0, 0.4, 1],
            outputRange: ['#1F1F34', '#FFD761', '#FA9502'],
          })}
          d="M16.0489 0.927051C16.3483 0.00574017 17.6517 0.00574017 17.9511 0.927051L21.2658 11.1287C21.3996 11.5407 21.7836 11.8197 22.2168 11.8197H32.9434C33.9122 11.8197 34.3149 13.0593 33.5312 13.6287L24.8532 19.9336C24.5027 20.1883 24.3561 20.6396 24.4899 21.0517L27.8046 31.2533C28.104 32.1746 27.0495 32.9407 26.2658 32.3713L17.5878 26.0664C17.2373 25.8117 16.7627 25.8117 16.4122 26.0664L7.73419 32.3713C6.95048 32.9407 5.896 32.1746 6.19535 31.2533L9.51006 21.0517C9.64393 20.6396 9.49728 20.1883 9.14679 19.9336L0.468768 13.6287C-0.314945 13.0593 0.0878303 11.8197 1.05655 11.8197H11.7832C12.2164 11.8197 12.6004 11.5407 12.7342 11.1287L16.0489 0.927051Z"
        />
      </Svg>
    </SafeAreaView>
  );

buschco@a1cbc20#diff-3ad04430c13b851d216fa0f686b4f9e4a7d013b50d492d9b27d969ccef669b74

Hey @buschco could you share an example of how you make it work with react-native's animated please I'm struggling to figure it out.

oiver555 commented 10 months ago

@MarlonAEC I spent went way too long on this example lol, hope it helps! https://snack.expo.dev/@oiver55/svg-reanimated-example-path-fill?platform=android

MarlonAEC commented 10 months ago

Hey @oiver555 I really love the effort you put into this example actually it helped me understand a bunch of things from react-native-reanimated. Now the thing is that I was looking for a way to do it using this Animated from react-native NOT the one from react-native-reanimated. Thanks for your example again it showed me a bunch of cool stuff I haven't tried with reanimited library! Super helpful!

vkukade-altir commented 10 months ago

@WoLewicki As you pointed out about adaptors.

I am facing one issue related to react-native-svg and reanimated. I am using SvgXml to dynamically render and change the svgs on UI thread. For that, I am using useAnimatedProps. But somehow it is not working. Does it have to do something with adaptor?

I have used following code :


import { SvgXml } from 'react-native-svg';

const AnimatedSVGXml = withAnimated(SvgXml); //withAnimated wraps SvgXml into class component

const svgXml = useSharedValue<string>(EmptySVG);

 useAnimatedReaction(
      () => {
        return position;
      },
      (currentValue, previousValue) => {
        if (currentValue !== previousValue) {
          console.log('in useAnimatedReaction svg value', currentValue, previousValue);
          //based on changes in position, I am updating svg 
          svgXml.value = currentValue;
        }
      },
    );

const animatedSvgProps = useAnimatedProps(() => {
      console.log('svgXml.value', svgXml.value); 
      // here I see that svgXml.value is updated. But not updated on screen. But if I remove svgXml.value and give the SVG directly it renders.
      return {
        xml: svgXml.value,
        //xml : '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>'
      };
    });

<AnimatedSVGXml animatedProps={animatedSvgProps} />
FashMuyhee commented 5 months ago
const ellipseAnimatedProps = 
   useAnimatedProps(() => 
   {
     const coordinates = {cx: 50, cy: 50, rx: 40, ry: 40};

     return {
       cx: coordinates.cx,
       cy: coordinates.cy,
       rx: coordinates.rx,
       ry: coordinates.ry,
       stroke: 'rgb(255,0,0)',
       fill: 'yellow',
       opacity: offset.value,
       strokeWidth: 2,
     };
   }
   , [], createAnimatedPropAdapter(
    (props) => {
      if (Object.keys(props).includes('fill')) {
        props.fill = {type: 0, payload: processColor(props.fill)}
      }
      if (Object.keys(props).includes('stroke')) {
        props.stroke = {type: 0, payload: processColor(props.stroke)}
      }
    },

Wow a hidden gem! I was about to say that this solution should be put on the docs, but I checked and it's already there :). You know when you have this issue you don't really know inherently to look into the "createAnimatedPropAdapter" to get the solution but I'm glad I found it either way!

This solved my problem. Thanks

JohnSawiris commented 4 months ago

@MarlonAEC I spent went way too long on this example lol, hope it helps! https://snack.expo.dev/@oiver55/svg-reanimated-example-path-fill?platform=android

Your example helped me fix my issue with Svg.Path. In my case I wanted to transition the fill color when a condition is true or false. I used useSharedValue with interpolateColor it worked perfectly on both android and iOS.

const animationFill = useSharedValue(1);

fill: interpolateColor(
  animationFill.value,
  [0, 0.2, 0.5, 1],
  [randomColor1, randomColor2, randomColor3, randomColor4]
 )

Thank you for taking the time to put this example together!

brunomaismei commented 4 months ago

As pointed in the #1845 (comment), the best way to handle fill and stroke props in by using createAnimatedPropAdapter which was made for this particular case, where props on the native side have different structure than in JS. Also, I changed the value prop to payload so it does not confuse reanimated babel plugin. Your animatedProps should look e.g. like this:

const ellipseAnimatedProps = 
   useAnimatedProps(() => 
   {
     const coordinates = {cx: 50, cy: 50, rx: 40, ry: 40};

     return {
       cx: coordinates.cx,
       cy: coordinates.cy,
       rx: coordinates.rx,
       ry: coordinates.ry,
       stroke: 'rgb(255,0,0)',
       fill: 'yellow',
       opacity: offset.value,
       strokeWidth: 2,
     };
   }
   , [], createAnimatedPropAdapter(
    (props) => {
      if (Object.keys(props).includes('fill')) {
        props.fill = {type: 0, payload: processColor(props.fill)}
      }
      if (Object.keys(props).includes('stroke')) {
        props.stroke = {type: 0, payload: processColor(props.stroke)}
      }
    },
    ['fill', 'stroke']));

This changed my svg form

` const animatedProps = useAnimatedProps( () => { const fillValue = interpolateColor( scrollY.value, [0, 150], [styleguide.colors.maiswhite, styleguide.colors.maismata500] ); return { fill: fillValue, }; }, [], createAnimatedPropAdapter( props => { if (Object.keys(props).includes("fill")) { props.fill = { type: 0, payload: props.fill }; } if (Object.keys(props).includes("stroke")) { props.stroke = { type: 0, payload: props.stroke }; } return props; }, ["fill", "stroke"] ) );

return ( <Animated.View style={[styles.backHeader, animatedStyle]}>

</Animated.View>`