The TikTok app has a pretty interesting loading animation. It has two horizontally aligned circles that seem to rotate its positions, seemingly in a circle. I wanted to create a similar behavior in React Native. The source code can be found here.
Initially, the spinner consists of two colored circles.
As soon as the red circle moves “below” the blue one, its overlapping shape turns into the background color. A similar “black” effect could be achieved by using the mix-blend-mode CSS property, if you are on the web. However, this does not exist in React Native.
The red circle moves all the way through the blue circle.
At half time, the red circle is left to the red circle. This triggers the back animation, so that the red circle moves back to its initial position.
First, we need to declare the shared variables. For managing the animated values, we use the
useSharedValue hook. This has a behavior similar to
The following code shows how to change a shared animated value. After the component has been mounted, we start the animation timer. Using a combination of
withTiming, the timer counts from 1 to -1 and back, in a loop.
Depending on the current time value, we need to change the
radius values. The library changes values automatically, means that you do not need to trigger most things, following the declarative concept. However, you need to put those changes (to change animated style or props) in specific hooks. In this example, we only need to change the component's props, so we will use
To actually change
x, we will use interpolation. We will simply "map" the input range from -1 to 1 to a specific output range. For x, we switch both circle's positions. For the
radius, we change according to the initial value. Similar things apply to the second circle.
In order to change component props via animated shared values, you need to pass the
useAnimatedProps output to the component. This will only work, when the component is actually animated via
Animated.createAnimatedComponent and you change native props of native views.
The circles are now animated, but still need to have the clipping effect. In order to achieve this, we need to define a
ClipPath mask, that includes both animated circles.
Finally, we will render three circles. First is for the render red circle. The last two are for the green one and its background, which have the same position and size. The green circle applies the clip path. As you might notice, one output of
useAnimatedProps could only be applied to one component, so the props had to be duplicated. This feels like a hack, but makes sense if you view it from a native perspective.
Originally published at https://mariusreimer.com on December 10, 2020.