Creating a star field with Reanimated 2
Photo by Mindaugas Vitkus on Unsplash
React Native includes the excellent Animated API. It has a declarative serialisable interface which means you pre-define your animations and they can be offloaded to run in native code (see useNativeDriver). This is necessary for smooth animations as code running in React Native’s Javascript thread can struggle to keep up with the screen’s refresh rate.
However, Reanimated 2 takes a different approach and runs code in a separate JavaScript context that runs on its own thread. This means you can write your own procedural animation code that results in smooth transitions.
Project Setup
Firstly make sure you have installed the Expo cli. Then create a new Expo app using the Reanimated v2 alpha template.
$ npx crna --template with-reanimated2
Be careful if experimenting with different Reanimated versions as Expo apps include specific versions of the native code for supported libraries, meaning different JS versions may not work. This tutorial is using Expo 40.0.0 and Reanimated 2.0.0-rc0.
TypeScript
Converting over to a TypeScript project is simple. If you’re not familiar with TypeScript, I recommend becoming familiar. It will save you time in the long run with autocompletion and linting. Run these commands to grab Expo’s default TypeScript config and convert the new app:
Show me the stars
Let's start with something simple, a single star (spoiler: it's actually a square).
Replace the contents of App.tsx with the following:
Admittedly, the results are underwhelming.
Make it move
Now we get to the interesting part. We’ll modify the above code to animate the star in a circular motion. Note that the Star component now returns an Animated.View (imported from react-native-animation). The useAnimatedStyle hook generates any style properties that should be animated. For performance, the static style properties are not included with the animated style.
Before the above code will work, we need to create the shared time value with the Reanimated useSharedValue hook. To animate the time, we use the React useEffect hook to trigger a repeating animation (withRepeat, withTiming), fire-and-forget style. It will loop after an hour, but chances are nobody will watch it for that long to see your animation pop! Don’t forget to pass this time value as a prop to the Star component.
It moves!
I want more
A single star moving is not particularly impressive, so let’s create a few more. Split the static star data into its own interface and add an id (more on that later). Create an array of star StarData and fill it with random positions.
Finally, alter the animated style code slightly to incorporate the id property as an offset to the sin and cos input. This stops the stars from all circling in unison.
And in the Starfield component, we need to iterate through the stars array and pass the star data in as props
Loop hitch due to video trimming
You call that a star field?
To make our moving squares a little more star-like, it’s a matter of animating them along the (virtual) z-axis and applying a perspective divide. Rather than add another z property to the stars, we can use use id / starCount to spread the stars evenly along the z axis. Since they are randomly placed in x and y, this doesn’t look too orderly. We also move the screen size calculation into the animated style function as it’s easier to apply the pseudo-3D maths to points in the [-0.5, 0.5] range.
It looks 3D!
That’s all folks
If you’d like to see the full final App.tsx code, it’s available here. In future posts I plan to explore other features of Reanimated 2. In particular, createAnimatedComponent and useAnimatedProps can be combined to create interesting animated SVG effects.