Animating Gradients in React Native

Image for post
Image for post

I’m working on a personal project called KaoCards, a flashcard app for remembering people’s names and faces. When I designed the product I thought it’d be cool to have an animated gradient as the background, but when I tried to implement, it turned out to be more work than I thought.

So here’s the story of how I managed to get it working. If you want the TL;DR, I made a repository you can follow along.

A naive attempt.

If you want to understand this, it’s important to know why my first attempt didn’t work.

To display a gradient in React Native, people use a project called react-native-linear-gradient. I wanted to see if anybody had tried animating the colors, and I found the Animated Gradient Transition example on that repo. Looking through the code, I had this reaction:

Image for post
Image for post

I didn’t understand why it needed two classes, why there was so much code. I didn’t think you’d need that much. I decided I wasn’t going to bother understanding all of that. I figured the way to get this done should be quite simple:

  1. Create an AnimatedLinearGradient component using Animate.createAnimatedComponent
  2. Interpolate some colors and pass them to AnimatedLinearGradient

Simple, right? Here’s the source (it will crash). I made an experimental app, ran it, and then, womp womp. We get the error:

JSON value `<null>` of type NSNull cannot be converted to a UIColor. Did you forget to call processColor() on the JS side?

Something somewhere down the line wasn’t getting colors values. So I turned to Twitter for help:

Jason Brown responded with the key insight:

Aaahhh ok! Animated doesn’t work with arrays. Although I thought I was doing everything correctly, the Animated library doesn’t process the values of array props, so the underlying native component is getting garbage instead of getting animated colors.

It became clear why the original example was so big.

Building it correctly.

Ok, understanding this limitation about Animated, let’s modify our game plan and make it a little more robust.

  1. We want our main component, AnimatedGradient, to work just like LinearGradient. It should take an array of colors.
  2. We want a transition to occur when we change the colors prop. To do this, AnimatedGradient needs to keep track of the previous colors.
  3. Since we can’t animate values in arrays, we can build a GradientHelper component that takes colors individually, and call Animated.createAnimatedComponent on that. GradientHelper will put the values into an array and pass them to the LinearGradient component from the react-native-linear-gradient package.

To keep things simple for this example, we’re going to assume that the colors array only has 2 values.

AnimatedGradient component

Source Code

First, we’ll create an AnimatedGradientHelper out of our GradientHelper, which we'll make in a bit.

Image for post
Image for post

In the constructor of AnimatedGradient, we'll initialize a prevColors state field to keep track of the previous colors. We also initialize an Animated.Value called tweener.

Image for post
Image for post

In getDerivedStateFromProps, we take the state.colors value and stick in state.prevColors. We set the new state.colors, and we reset the tweener.

Image for post
Image for post

In componentWillUpdate (aka when props change), we'll make the tweener move from 0 to 1.

Image for post
Image for post

In the render method, we use the tweener, prevColors, and colors to create two color interpolations, and pass them individually to our AnimatedGradientHelper.

Image for post
Image for post

Gradient Helper

SourceCode

In GradientHelper, all we're doing is taking the color1 and color2 props, putting them into an array, and passing that to LinearGradient. We're doing this because we need to get around Animated's limitations.

Image for post
Image for post

And that’s the gist of it. Here’s the demo:

Image for post
Image for post

Now we know why the original example is so big. It had to do all this stuff, and handle gradients with more than 2 colors.

But wait, what else can we animate?

We can actually go the extra step and animate other properties. The LinearGradient component lets you specify coordinates for the start and end of the gradient. Why not interpolate those too? Here’s an updated render method. You can probably guess what happened in the rest of the component. Source

Image for post
Image for post

We’ll just need to tweak our GradientHelper a bit by doing something with the props. Source

Image for post
Image for post

And now we have a cooler demo.

Image for post
Image for post

I was able to combine this animated gradient with some other animations to create a cool background effect for my KaoCards project:

So, now you know how to animate gradients in React Native, and a little bit more about how Animated works. What other properties do you think you can animate?

Thanks to Jason Brown for providing the key insight. Everything I’ve learned about animating stuff in React Native I’ve learned from him. Make sure you follow him on Twitter, and check out his React Native Animations course.

Startup survivor, drinker of the MIT firehose. React Native developer. geosocial.io is my startup dream. If at first you don’t succeed, sudo bang bang.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store