Animated Gradient in React Native (Skia) Hero Image

Animated Gradient in React Native (Skia)

Published on
| Minutes Read: 13 min
Authors

Are you a mobile app developer looking to add a simple yet powerful linear gradient animation to your React Native project? Look no further. In this tutorial, we'll guide you through the process of creating a beautiful linear gradient animation using React Native, Reanimated, and Skia. This animation might seem trivial, but it's a great example of how using the right tools can greatly simplify your work.

Before we dive into the code, I want to take a moment to express my gratitude to my patrons for their support. Your support allows me to create more in-depth tutorials like this one. If you're a fan of React Native animations, consider supporting me on Patreon, where I post the source code of a new animation every week.

Feel free to check directly the source code:

YouTube Channel

Are you more of a visual learner? Check out the video version of this tutorial on my YouTube channel:

Project Overview

For this tutorial, I've already set up a React Native project using Expo. I've defined a few things, including a TouchableOpacity component, which logs "pressed" when tapped. The styling for this component is straightforward, using absolute positioning with a custom icon from Expo Vector Icons.

return (
  <>
    <StatusBar style="light" />
    <View style={{ flex: 1 }} />
    <TouchableOpacity
      style={styles.buttonContainer}
      onPress={() => {
        console.log('pressed')
      }}
    >
      <FontAwesome name="random" size={24} color="white" />
    </TouchableOpacity>
  </>
)

To make this animation work, we'll use the Reanimated and React Native Skia packages. Make sure you have a version of Reanimated above 3.0, as the interaction between shared values from Reanimated and Skia components relies on this version. Don't forget to add the Reanimated plugin to your Babel configuration to avoid crashes.

Adding the Linear Gradient

Let's start by defining our linear gradient. The linear gradient is a component imported from React Native Skia, and it should be placed inside a Skia Canvas. The Canvas will take all available space, so we can start by defining it with a flex: 1 style.

import { Canvas, LinearGradient, Rect, vec } from '@shopify/react-native-skia'

// ...
return (
  <>
    <StatusBar style="light" />
    <Canvas style={{ flex: 1 }}>
      <Rect x={0} y={0} width={200} height={200}>
        <LinearGradient start={vec(0, 0)} end={vec(200, 200)} colors={['red', 'blue']} />
      </Rect>
    </Canvas>
    ...
  </>
)

At this point, the Canvas is taking up the space, but the Rect is not. To set the Rect dimensions, we need to retrieve the width and height of the screen using the useWindowDimensions hook from React Native.

We also need to assign the screen width and height to the width and height of the LinearGradient.

import { useWindowDimensions } from 'react-native'

return (
  <>
    <StatusBar style="light" />
    <Canvas style={{ flex: 1 }}>
      <Rect x={0} y={0} width={width} height={height}>
        <LinearGradient start={vec(0, 0)} end={vec(width, height)} colors={['red', 'blue']} />
      </Rect>
    </Canvas>
    ...
  </>
)

Animating the Gradient Colors

This isn't enough to create the gradient animation. To do that, we'll use Reanimated. We'll define two shared values, leftColor and rightColor, which will represent the colors on the left and right sides of the gradient.

import { useSharedValue, withTiming } from 'react-native-reanimated'

// ...

const leftColor = useSharedValue('red')
const rightColor = useSharedValue('blue')

To animate these colors, we can create a derived value, colors, which will be an array containing the values of leftColor and rightColor. This is where the magic happens.

const colors = useDerivedValue(() => {
  return [leftColor.value, rightColor.value]
}, [])

Now that we have the colors derived value, we can apply it to the LinearGradient within the Rect. The colors of the gradient will change based on the values of leftColor and rightColor.

<Canvas style={{ flex: 1 }}>
  <Rect x={0} y={0} width={width} height={height}>
    <LinearGradient start={vec(0, 0)} end={vec(width, height)} colors={colors} />
  </Rect>
</Canvas>

Animating the Gradient

To create a smooth animation, we'll use the withTiming function from Reanimated. When the TouchableOpacity is pressed, we'll update the leftColor and rightColor values with new random colors.

<TouchableOpacity
  style={styles.buttonContainer}
  onPress={() => {
    leftColor.value = withTiming(getRandomColor())
    rightColor.value = withTiming(getRandomColor())
  }}
>
  <FontAwesome name="random" size={24} color="white" />
</TouchableOpacity>

The getRandomColor function generates random colors for us to apply to the gradient. This ensures that the animation doesn't use fixed colors.

export const getRandomColor = () => {
  // Generate random RGB color values
  const r = Math.floor(Math.random() * 256)
  const g = Math.floor(Math.random() * 256)
  const b = Math.floor(Math.random() * 256)

  // Return the color in the format '#RRGGBB'
  return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`
}

With this setup, the gradient animation smoothly transitions between colors whenever the TouchableOpacity is pressed.

Full Recap

Did you miss something? Here's the full recap of the code we've written so far:

import { TouchableOpacity, StyleSheet, View, useWindowDimensions } from 'react-native'
import { StatusBar } from 'expo-status-bar'
import { FontAwesome } from '@expo/vector-icons'
import { Canvas, LinearGradient, Rect, vec } from '@shopify/react-native-skia'
import { useDerivedValue, useSharedValue, withTiming } from 'react-native-reanimated'

// That was in the utils file (but let's keep it here for the sake of simplicity)
const getRandomColor = () => {
  // Generate random RGB color values
  const r = Math.floor(Math.random() * 256)
  const g = Math.floor(Math.random() * 256)
  const b = Math.floor(Math.random() * 256)

  // Return the color in the format '#RRGGBB'
  return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`
}

const App = () => {
  const { width, height } = useWindowDimensions()

  const leftColor = useSharedValue('red')
  const rightColor = useSharedValue('blue')

  const colors = useDerivedValue(() => {
    return [leftColor.value, rightColor.value]
  }, [])

  return (
    <>
      <StatusBar style="light" />
      <Canvas style={{ flex: 1 }}>
        <Rect x={0} y={0} width={width} height={height}>
          <LinearGradient start={vec(0, 0)} end={vec(width, height)} colors={colors} />
        </Rect>
      </Canvas>
      <TouchableOpacity
        style={styles.buttonContainer}
        onPress={() => {
          // Where the magic happens 🪄
          leftColor.value = withTiming(getRandomColor())
          rightColor.value = withTiming(getRandomColor())
        }}
      >
        <FontAwesome name="random" size={24} color="white" />
      </TouchableOpacity>
    </>
  )
}

// Boring styles
const styles = StyleSheet.create({
  buttonContainer: {
    position: 'absolute',
    bottom: 52,
    right: 32,
    height: 64,
    aspectRatio: 1,
    borderRadius: 40,
    backgroundColor: '#111',
    zIndex: 10,
    justifyContent: 'center',
    alignItems: 'center',
  },
})

export { App }

Conclusion

That's it! You've successfully created a beautiful linear gradient animation using React Native, Reanimated, and Skia. This animation can be a valuable addition to a wide range of use cases in your mobile app projects. If you have any suggestions for future video tutorials, feel free to share them in the comments section. Thank you for joining me in this tutorial, and I look forward to seeing you in the next one.

Join me on Patreon and be a part of the community 🎊

  • More than 50+ exclusive animations made with Reanimated, Gesture Handler and React Native Skia
  • Get access to my newsletter and be the first to know about new content
  • Join a community of like-minded individuals passionate about React Native