Last month, Software Mansion announced the alpha release of Reanimated 2. Version 2 is a complete paradigm shift in the way we build gestures and animations in React Native.
Last January, at the React Native Community meetup in Kraków, Krzysztof Magiera (co-creator of React Native Android and creator of Gesture Handler and Reanimated) mentioned the idea of a new implementation of Reanimated based on a concept borrowed from an experimental web API, animation worklets: JavaScript functions that run on an isolated context to compute an animation frame. And a few days later Shopify announced its support for Software Mansion’s effort in the open source community, including backing the new implementation of Reanimated.
When writing gestures and animations in React Native, the key to success is to run the complete user interaction on the UI thread. This means that you don’t need to communicate with the JavaScript thread, nor expect this thread to be free to compute an animation frame.
In Reanimated 1, the strategy employed was to use a declarative domain-specific language to declare gestures and animations beforehand. This approach is powerful, but came with drawbacks:
- learning curve is steep
- some limitations in the available set of instructions
- performance issues at initialization time.
To use the Reanimated v1 Domain Specific Language, you have to adopt a declarative mindset which is challenging, and simple instructions could end-up being quite verbose. Basic mathematical tools such as coordinate conversions, trigonometry, and bezier curves, just to name a few, had to be rewritten using the declarative DSL.
And while the instruction set offered by v1 is large, there are still some use cases where you are forced to cross the React Native async bridge (see Understanding the React Native bridge concept), for example, when doing date or number formatting for instance.
Finally, the declaration of the animations at initialization time has a performance cost: the more animation nodes are created, the more messages need to be exchanged between the JavaScript and UI thread. On top of that, you need to take care of memoization: making sure to not re-declare animation nodes more than necessary. Memoization in v1 proved to be challenging and a substantial source of potential bugs when writing animations.
Enter Reanimated 2.
Animation Worklets
One of the interesting takeaways from the official announcement is that Software Mansion approached the problem from a different angle. Instead of starting from the main constraint, to not cross the React Native bridge, and offering a way to circumvent it, they asked: How would it look if there were no limitations when writing gestures and animations? They started the work on a solution from there.
Reanimated 2 is based on a new API named animation worklets. These are JavaScript functions that run on the UI thread independently from the JavaScript thread. These functions can be declared as a worklet via the worklet
directive.
Worklets can receive parameters, access constants from the JavaScript thread, invoke other worklet functions, and invoke functions from the JavaScript thread asynchronously. This new API might remind you of web workers which are also isolated JS functions that talk to the main thread via asynchronous messages. They may also remind you of OpenGL shaders which are also pieces of code to be compiled and executed in an independent manner.
The code snippet above showcases six properties from worklets. Worklets:
- run on the UI thread.
- are declared via the ‘worklet’ directive.
- can be invoked from the JS thread and receive parameters.
- can invoke other worklet functions synchronously.
- can access constants from the JS thread.
- can asynchronously call functions from the JS thread.
Reanimated 2 liquid-swipe example - click here to see animation
The team at Software Mansion is offering us a couple of great examples to showcase the new Reanimated API. An interesting one is the liquid-swipe example. It features a couple of advanced animation techniques such as bezier curve interpolation, gesture handling, and physics-based animations, showing us that Reanimated 2 is ready for any kind of advanced gestures and animations
The API
When writing gestures and animations, you need to do three things:
- create animation values
- handle gesture states
- assign animation values to component properties.
The new Reanimated API is offering us five new hooks to perform these three tasks.
Create Values
There are two hooks available to create animation values. useSharedValue()
creates a shared value that are like Animated.Value
but they exist in both the JavaScript and the UI thread. Hence the name.
The useDerivedValue()
hook creates a shared value based on some worklet execution. For instance, in the code snippet below, the theta
value is computed in a worklet.
Handle Gesture States
useAnimatedGestureHandler()
hook can connect worklets to any gesture handler from react-native-gesture-handler. Each event implements a callback with two parameters: event, which contains the values of the gesture handler, and context which you can conveniently use to keep some state across gesture events
Assign Values to Properties
useAnimatedStyle()
returns a style object that can be assigned to an animated component.
Finally, useAnimatedProps()
is similar to useAnimatedStyle but for animated properties. Now animated props are set via the animatedProps property
A Reanimated 2 Animation Example
Let’s build our first example with Reanimated 2. We have an object that we want to drag around the screen. Like in v1, we create two values for us to translate the card on the x and y axis, and we wrap the card component with a PanGesture
handler. So far so good.
As seen in the above code, the animation values are created using useSharedValue
and assigned to an animated view via useAnimatedStyle
. Now let’s create a gesture handler via useAnimatedGestureHandler
. In our gesture handler, we want to do three things:
- When the gesture starts, we store the
translate
values into the context object. This allows us to keep track of the cumulated translations across different gestures. - When the gesture is active, we assign to
translate
the gesture translation plus its offset values. - When the gesture is released, we want to add a decay animation based on its velocity to give the effect that the view moves like a real physical object
The final example source can be found on GitHub. You can see the gesture in action below.
Reanimated 2 PanGesture example - click here to see the animation
Going Forward
With Reanimated 2, the team at Software Mansion asked: “How would it look if there were no constraints when writing gestures and animations in React Native?” and started from there.
This new version is based on the concept of animation worklets, JavaScript functions that are executed on the UI thread independently from the JavaScript thread. Worklets can receive parameters, access constants, and asynchronously invoke functions from your React code. The new Reanimated API offers five new hooks to create animation values, gesture handlers, and assign animated values to component properties. Part of the Github repository, are many examples that showcase the power of the new Reanimated API.
We hope that you are as excited about this new version as we are. Reanimated 2 dramatically lowers the barrier to entry in building complex user-interactions in React Native. It also enables new use-cases where we previously had to cross the React Native bridge (to format values for instance). It also dramatically improves the performance at initialization time which in the future might have a substantial impact on particular tasks such as navigation transitions.
We are looking forward to following the progress of this new exciting way to write gestures and animations.
We're always on the lookout for talent and we’d love to hear from you. Visit our Engineering career page to find out about our open positions.