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

Keyframe Animation only works with StyleSheet.create and within on Animated.View

Open EyMaddis opened this issue 3 years ago • 6 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Describe the issue

(this might be too issues, but I guess that they are probably based on the same underlying problem) Hi 👋

I recently upgraded from 0.11 to 0.18 and noticed that our loading indicators are broken (no longer animated). I noticed that the CSS property animationKeyframes is only respected if

  1. The element is a View (Animated.View does not work)
  2. The animation is passed through StyleSheet.create, inline styling does not work

The generated CSS contains all CSS animation properties (like animation-timing-function) except animation-name.

Expected behavior

Animation keyframes should be generated and work when using inline styles and/or Animated.View. All Views should rotate in the provided test case.

Steps to reproduce

Use React Native Web 0.18.0. (See attached codesandbox)

const animation = {
  animationKeyframes: [
    {
      "0%": { transform: [{ rotate: "0deg" }] },
      "100%": { transform: [{ rotate: "360deg" }] }
    }
  ],
  animationDuration: "750ms",
  animationIterationCount: "infinite",
  animationTimingFunction: "linear",
  animationPlayState: "running"
};

const Styles = StyleSheet.create({
  animation
});

then render views


      <Animated.View
        style={[
          {
            width: 100,
            height: 100,
            backgroundColor: "red"
            //...animation
          }
        ]}
      >
        <Text>Animated.View inline!</Text>
      </Animated.View>
      <Animated.View
        style={[
          {
            width: 100,
            height: 100,
            backgroundColor: "red"
          },
          Styles.animation
        ]}
      >
        <Text>Animated.View StyleSheet!</Text>
      </Animated.View>
      <View
        style={[
          {
            width: 100,
            height: 100,
            backgroundColor: "green",
            ...animation
          }
        ]}
      >
        <Text>View inline!</Text>
      </View>
      <View
        style={[
          {
            width: 100,
            height: 100,
            backgroundColor: "green"
          },
          Styles.animation
        ]}
      >
        <Text>View StyleSheet!</Text>
      </View>

Observe that only the last View (with Text "View StyleSheet!") will start to rotate.

Test case

https://codesandbox.io/s/react-native-keyframe-animation-bug-ts4ib9

Additional comments

Thanks for being such a pillar of the React (Native) community!

EyMaddis avatar Aug 29 '22 14:08 EyMaddis

FWIW inline style animations haven't been supported for a long time. I'll look into why the Animated one doesn't work

necolas avatar Aug 29 '22 17:08 necolas

Oh it's because Animated calls StyleSheet.flatten which produces a new style object no longer recognized by the compiler (which doesn't produce class names at render time anymore, for React 18 reasons). You can consider Animated as only supporting inline styles at the moment. Is there a reason why you're trying to use CSS Animations with the Animated API, rather than using only one or the other?

necolas avatar Aug 29 '22 18:08 necolas

I might have a fix for this. I'll hopefully get a patch up soon for you to try in your environment

necolas avatar Aug 29 '22 18:08 necolas

The reason for it was a single component for web and native, but using css animations on web and native Animated otherwise. I could have done it differently but there was no reason for completely separate components.

Using Animated for the loading indicator was especially fragile as the indicator hides a lot of JS main thread work.

There is also a loading placeholder component that can get the opacity animated value from the parent, while also having an additional css animation that runs every time. I solved that by wrapping it into another Animated.View.

Is there a specific reason why inline animations are not supported? (I guess performance?) I find it a bit weird that animation-name is just missing. Could there at least be a warning or is it hard to detect?

EyMaddis avatar Aug 29 '22 20:08 EyMaddis

I could have done it differently but there was no reason for completely separate components.

Why not use View for web and Animated.View for native? That seems like a relatively simple conditional that avoids the overhead of all the Animated implementation on web if you're not using it at all

Is there a specific reason why inline animations are not supported? (I guess performance?)

Because inline styles are not converted to CSS rules, but animations require CSS rules and keyframes.

necolas avatar Aug 29 '22 20:08 necolas

Why not use View for web and Animated.View for native?

🤷‍♂️ Well, now I am since 0.18 😜

Thanks for the insight, makes sense!

EyMaddis avatar Aug 29 '22 21:08 EyMaddis

Awesome, you are the best, @necolas! 👍

EyMaddis avatar Mar 27 '23 21:03 EyMaddis