react-native-reanimated: [v2] rotate transform (withTiming) exception on Android

Description

The following snippet throws an exception on Android but works on iOS.

Exception: Invariant Violation: Transform with key of “rotateZ” must be a string: {“rotateZ”:0} Adding deg or rad to the string just crashes the app instead.

const animatedStyle = useAnimatedStyle(() => {
    const toValue = isOpen.value ? Math.PI / 4 : 0;
    const rotateZ = withTiming(toValue, {
      duration: 100,
      easing: Easing.linear,
    });

    return {
      transform: [
        {
          rotateZ,
        },
      ],
    };
});

Screenshots

image

Steps To Reproduce

use any of the rotate transforms on Android and apply withTiming

Package versions

  • React: 16.13.1
  • React Native: 0.63.2
  • React Native Reanimated: 2.0.0-alpha.4

Logcat

 Error while updating prop transform
    java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.facebook.react.uimanager.ViewManagersPropertyCache$PropSetter.updateViewProp(ViewManagersPropertyCache.java:87)
        at com.facebook.react.uimanager.ViewManagerPropertyUpdater$FallbackViewManagerSetter.setProperty(ViewManagerPropertyUpdater.java:136)
        at com.facebook.react.uimanager.ViewManagerPropertyUpdater.updateProps(ViewManagerPropertyUpdater.java:56)
        at com.facebook.react.uimanager.ViewManager.updateProperties(ViewManager.java:49)
        at com.facebook.react.uimanager.NativeViewHierarchyManager.updateProperties(NativeViewHierarchyManager.java:137)
        at com.facebook.react.uimanager.UIImplementation.synchronouslyUpdateViewOnUIThread(UIImplementation.java:291)
        at com.swmansion.reanimated.NodesManager.updateProps(NodesManager.java:473)
        at com.swmansion.reanimated.NativeProxy.updateProps(NativeProxy.java:91)
        at com.swmansion.reanimated.NativeProxy$AnimationFrameCallback.onAnimationFrame(Native Method)
        at com.swmansion.reanimated.NodesManager.onAnimationFrame(NodesManager.java:174)
        at com.swmansion.reanimated.NodesManager.access$000(NodesManager.java:61)
        at com.swmansion.reanimated.NodesManager$1.doFrameGuarded(NodesManager.java:127)
        at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29)
        at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:175)
        at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:85)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:947)
        at android.view.Choreographer.doCallbacks(Choreographer.java:761)
        at android.view.Choreographer.doFrame(Choreographer.java:693)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.NumberFormatException: For input string: "[object Object]"
        at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
        at sun.misc.FloatingDecimal.parseFloat(FloatingDecimal.java:122)
        at java.lang.Float.parseFloat(Float.java:451)
        at com.facebook.react.uimanager.TransformHelper.convertToRadians(TransformHelper.java:40)
        at com.facebook.react.uimanager.TransformHelper.processTransform(TransformHelper.java:68)
        at com.facebook.react.uimanager.BaseViewManager.setTransformProperty(BaseViewManager.java:322)
        at com.facebook.react.uimanager.BaseViewManager.setTransform(BaseViewManager.java:82)
        at com.facebook.react.views.view.ReactViewManager.setTransform(ReactViewManager.java:272)
        at com.facebook.react.views.view.ReactViewManager.setTransform(ReactViewManager.java:37)
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.facebook.react.uimanager.ViewManagersPropertyCache$PropSetter.updateViewProp(ViewManagersPropertyCache.java:87) 
        at com.facebook.react.uimanager.ViewManagerPropertyUpdater$FallbackViewManagerSetter.setProperty(ViewManagerPropertyUpdater.java:136) 
        at com.facebook.react.uimanager.ViewManagerPropertyUpdater.updateProps(ViewManagerPropertyUpdater.java:56) 
        at com.facebook.react.uimanager.ViewManager.updateProperties(ViewManager.java:49) 
        at com.facebook.react.uimanager.NativeViewHierarchyManager.updateProperties(NativeViewHierarchyManager.java:137) 
        at com.facebook.react.uimanager.UIImplementation.synchronouslyUpdateViewOnUIThread(UIImplementation.java:291) 
        at com.swmansion.reanimated.NodesManager.updateProps(NodesManager.java:473) 
        at com.swmansion.reanimated.NativeProxy.updateProps(NativeProxy.java:91) 
        at com.swmansion.reanimated.NativeProxy$AnimationFrameCallback.onAnimationFrame(Native Method) 
        at com.swmansion.reanimated.NodesManager.onAnimationFrame(NodesManager.java:174) 
        at com.swmansion.reanimated.NodesManager.access$000(NodesManager.java:61) 
        at com.swmansion.reanimated.NodesManager$1.doFrameGuarded(NodesManager.java:127) 
        at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29) 
        at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:175) 
        at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:85) 
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:947) 
        at android.view.Choreographer.doCallbacks(Choreographer.java:761) 
        at android.view.Choreographer.doFrame(Choreographer.java:693) 
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935) 
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6669) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
    

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 5
  • Comments: 20 (5 by maintainers)

Most upvoted comments

Okay, so I think my usage is somewhat wrong (even though it works on iOS).

This works on both platforms

const rotateZ = useDerivedValue(() => {
  return withTiming(isOpen.value ? 45 : 0, {
    duration: 100,
    easing: Easing.linear,
  });
});

const animatedStyle = useAnimatedStyle(() => {
  return {
    transform: [{rotateZ: `${rotateZ.value}deg`}],
  };
});

I’m facing the same issue trying to make some rotation in rotateZ property.

Sample

    const starStyle = useAnimatedStyle(() => {
        const scaleArray = [
            withDelay(index * (delay / 2), withTiming(scale.value, { duration: 500, easing: Easing.bounce })),
            withDelay(500, withTiming(1, { duration: 500, easing: Easing.ease })),
        ];

        const rotateArray = [
            withDelay(index * (delay / 2), withTiming(0, { duration: 500, easing: Easing.ease })),
            withDelay(500, withTiming(rotate.value, { duration: 500, easing: Easing.ease })),
        ];

        if (index > rate) {
            scaleArray.shift();
            rotateArray.shift();
        } else if (rate === index) {
            scaleArray.pop();
            rotateArray.pop();
        }

        const opacityDelay = withDelay(index * (delay / 2), withTiming(opacity.value, { duration: 250 }));
        const rotateSequence = withSequence(...rotateArray);
        const scaleSequence = withSequence(...scaleArray);

        return {
            opacity: opacityDelay,
            transform: [
                { rotateZ: rotateSequence },
                {
                    scale: scaleSequence,
                },
            ],
        };
    }, [index, rate]);

EDIT: if wrapping in a string it crashes the app.

Okay, it seems the withTiming is what breaks it on Android.

I’m able to do the following without any issues

  • rotateZ: '${toValue}deg' //changed quotes so it shows on GH
  • rotateZ: '100deg'
  • rotateZ: '100rad'

This is still happening for me, but not only with rotate properties, I cannot use any kind of ‘transform’ property with a animated value, even this simplest example ever breaks on android:

  const scale = useSharedValue(1);
  const animatedScale = useAnimatedStyle(() => {
    return {
      transform: [{ scale: scale.value }],
    };
  });
  ...
  ...
  onPress={() => {
    scale.value = withTiming(0.94, { duration: 100 });
  }

The worst thing is that the ‘Examples’ repo of react-native-reanimated has an example doing exactly this and it works, and also, I created a mini-game and used something like this and it works. The only difference that I found between my app not working and the other two that do work is that the ones that work are using react-native 0.64 and the one that doesn’t is using 0.62.

EDIT: I took the time to upgrade from react-native 0.62.2 to 0.64.2 on my app and this problem got solved ! So for everybody reading this, it may be time to upgrade react-native, painful process but it is what it is! Cheers

Okay, so I think my usage is somewhat wrong (even though it works on iOS).

This works on both platforms

const rotateZ = useDerivedValue(() => {
  return withTiming(isOpen.value ? 45 : 0, {
    duration: 100,
    easing: Easing.linear,
  });
});

const animatedStyle = useAnimatedStyle(() => {
  return {
    transform: [{rotateZ: `${rotateZ.value}deg`}],
  };
});

Try to remove “easing: Easing.linear,” from here, that was the reason of my crashes.

@terrysahaidak I’m facing the same issue with the last version of Reanimated 2.0.0-alpha.9.2.

When I set it to rotate as a string the error is gone (Android) but it’s not rotated, Also iOS not rotated however if it’s Number Rotation work just in iOS.

Here’s the Repo you can check it if u wondering.

So it seems a TypeScript error is causing withTiming to not work with a user config (Easing is missing from the types), once ignoring this, then withTiming works fine however not on transform props (as mentioned above).

Repro for android (

import React from 'react';
import {View, Button} from 'react-native';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withTiming,
  Easing,
} from 'react-native-reanimated';

interface Props {}

export function Repro({}: Props) {
  const isOpen = useSharedValue(false);
  const onPress = React.useCallback(() => {
    isOpen.value = !isOpen.value;
  }, [isOpen]);

  const animatedStyle = useAnimatedStyle(() => {
    const toValue = isOpen.value ? 100 : 0;
    const timing = withTiming(toValue, {duration: 1000, easing: Easing.linear});

    return {
      transform: [
        {
          rotate: `${toValue}deg`, // deg required for android
          //rotate: timing, // this works for iOS not Android
        },
      ],
    };
  });

  return (
    <View style={{justifyContent: 'center', alignItems: 'center'}}>
      <Animated.View
        style={[
          {width: 100, height: 100, backgroundColor: 'red'},
          animatedStyle,
        ]}
      />
      <Button onPress={onPress} title="Toggle rotation" />
    </View>
  );
}

Oh sorry, missed it in the description.

So seems like I was wrong and Android expects string but we cannot run withTiming with string values.

/cc @kmagiera @Szymon20000