Mesh Gradient

A Skia-driven animated mesh gradient that smoothly blends up to four colors over time

Last updated on

Edit on GitHub

Manual

Install the following dependencies:

npm install react-native-reanimated @shopify/react-native-skia

Copy and paste the following code into your project. component/organisms/mesh-gradient.tsx

// @ts-checkimport React, { memo, useMemo } from "react";import { StyleSheet, View, useWindowDimensions } from "react-native";import { Canvas, Shader, Skia, Fill, vec } from "@shopify/react-native-skia";import {  useSharedValue,  useDerivedValue,  useFrameCallback,} from "react-native-reanimated";import { SHADER as MESH_GRADIENT_SHADER } from "./conf";import { DEFAULT_INITIAL_COLORS } from "./const";import type { IAnimatedMeshGradient, IMeshGradientColor } from "./types";export const AnimatedMeshGradient: React.FC<IAnimatedMeshGradient> &  React.FunctionComponent<IAnimatedMeshGradient> = memo<IAnimatedMeshGradient>(  ({    colors = DEFAULT_INITIAL_COLORS,    speed = 1,    noise = 0.15,    blur = 0.4,    contrast = 1,    animated = true,    style,    width: paramsWidth,    height: paramsHeight,  }: React.ComponentProps<typeof AnimatedMeshGradient>): React.ReactNode &    React.JSX.Element &    React.ReactElement => {    const { width: screenWidth, height: screenHeight } = useWindowDimensions();    const width = paramsWidth ?? screenWidth;    const height = paramsHeight ?? screenHeight;    const time = useSharedValue<number>(0);    useFrameCallback((frameInfo) => {      if (animated && frameInfo.timeSincePreviousFrame !== null) {        time.value += (frameInfo.timeSincePreviousFrame / 1000) * speed;      }    }, animated);    const safeColors = useMemo<IMeshGradientColor[]>(() => {      const result = [...colors];      while (result.length < 4) {        result.push(          DEFAULT_INITIAL_COLORS[result.length % DEFAULT_INITIAL_COLORS.length],        );      }      return result.slice(0, 4);    }, [colors]);    const shader = useMemo(() => {      return Skia.RuntimeEffect.Make(MESH_GRADIENT_SHADER);    }, []);    const uniforms = useDerivedValue(() => {      return {        resolution: vec(width, height),        time: time.value,        noise: Math.max(0, Math.min(1, noise)),        blur: Math.max(0, Math.min(1, blur)),        contrast: Math.max(0, Math.min(2, contrast)),        color1: [safeColors[0].r, safeColors[0].g, safeColors[0].b, 1],        color2: [safeColors[1].r, safeColors[1].g, safeColors[1].b, 1],        color3: [safeColors[2].r, safeColors[2].g, safeColors[2].b, 1],        color4: [safeColors[3].r, safeColors[3].g, safeColors[3].b, 1],      };    }, [width, height, noise, blur, contrast, safeColors, time]);    if (!shader) {      return <View style={[styles.container, style, { width, height }]} />;    }    return (      <View style={[styles.container, style, { width, height }]}>        <Canvas style={StyleSheet.absoluteFill}>          <Fill>            <Shader source={shader} uniforms={uniforms} />          </Fill>        </Canvas>      </View>    );  },);const styles = StyleSheet.create({  container: {    overflow: "hidden",  },});export default memo<  React.FC<IAnimatedMeshGradient> &    React.FunctionComponent<IAnimatedMeshGradient>>(AnimatedMeshGradient);

Usage

import * as React from "react";import { StyleSheet, View } from "react-native";import { AnimatedMeshGradient } from "@/components/organisms/mesh-gradient";import { IMeshGradientColor } from "@/components/organisms/mesh-gradient/types";export default function App() {  const colors: IMeshGradientColor[] = [    { r: 0.9, g: 0.5, b: 255 / 0.92 },    { r: 0.95, g: 0.4, b: 255 / 0.85 },    { r: 0.88, g: 0.7, b: 255 / 0.96 },    { r: 0.62, g: 0.1, b: 255 / 0.8 },  ];  return (    <View style={stylez.container}>      <AnimatedMeshGradient        speed={2}        contrast={0}        noise={0.5}        blur={0}        animated={true}        colors={colors}      />    </View>  );}const stylez = StyleSheet.create({  container: {    flex: 1,    justifyContent: "center",    alignItems: "center",  },});

Props

IMeshGradientColor

React Native Reanimated
React Native Skia

On this page