Invert Text Color Based on Background Color in React Native

In this blog post, we are going to learn how to use blend mode in React Native Skia

If we compare Flutter with React Native, Flutter's strong arguments are its smooth and graphic drawing support with Skia. But thanks to Shopify, William Candollin, Christian Falch, and the dev team behind react-native-skia, we can now use Skia in our React Native applications to draw graphics.

What is Skia?

Skia is a 2D graphics library that can be used to develop applications and user interface for mobile apps, websites, and embedded devices. It is an Open Source software available under Apache 2.0 License.

Getting started

Create a new react native project

We are starting from a fresh react native project.

npx react-native init blendmode

installing react-native-skia and react-native-reanimated

yarn add @shopify/react-native-skia
yarn add react-native-reanimated

Or using npm:

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

We need to run pod install to link iOS dependencies.

Let's import the necessary things from the packages.

import React, {useEffect} from 'react';
import {StyleSheet, View, useWindowDimensions} from 'react-native';
import {
  Canvas,
  useFont,
  useValue,
  useSharedValueEffect,
  useComputedValue,
  mix,
  Group,
  Rect,
  Text,
  Fill,
} from '@shopify/react-native-skia';
import {useSharedValue, withRepeat, withTiming} from 'react-native-reanimated';
import Font from './my-font.ttf';

you may notice the font is imported directly. You don't need to link to use fonts in Skia.

React Native Skia is using its own React renderer. React Native Skia supports animations through the concept of Skia Values. It seems to me it's very similar to  React Native Reanimated mental modal.

Since React Native Skia has its own renderer. It needs Canvas to draw. Canvas component is the root of your Skia drawing.

export default () => {
  const font = useFont(Font, 64);
  const progress = useSharedValue(0);
  const progressWidth = useValue(0);
  const {width} = useWindowDimensions();
  useEffect(() => {
    progress.value = withRepeat(withTiming(1, {duration: 5000}), -1, true);
  }, [progress]);

  useSharedValueEffect(() => {
    progressWidth.current = mix(progress.value, 0, width);
  }, [progress]);
  const progressComputed = useComputedValue(() => {
    return mix(progress.value, 0, 100).toFixed().toString();
  }, [progressWidth]);

  if (font === null) {
    return null;
  }
  return (
    <View style={{flex: 1, backgroundColor: '#ffa', justifyContent: 'center'}}>
      <Canvas style={styles.canvas}>
        <Fill color="white"></Fill>
        <Group blendMode="difference">
          <Rect
            x={0}
            y={0}
            width={progressWidth}
            height={300}
            color="#00ffff"
          />
          <Text
            color="#00ffff"
            x={150}
            y={155}
            font={font}
            text={progressComputed}></Text>
        </Group>
      </Canvas>
    </View>
  );
};

const styles = StyleSheet.create({
  canvas: {
    height: 300,
  },
});

In the code above,  we are animating the progress value from zero to one forever, and then we have a callback invoked on progress value change thanks to the useSharedValueEffect hook. it gives us the  <Rect>  current width which is progressWidth.   the mix  the function here is responsible to apply linear interpolation. We also need to use the useComputedValue  hook to calculate the Text component value when progressWidth changes.

Let's do the magic part.

until now we just created a simple progress bar. We didn't show yet React Native Skia power.

as you can see above code. We have Canvas and Group components. The group component is a building block in React Native Skia. You can nest the component with the Group component. Group  component has a prop named blendMode

blendMode property sets how an element's content should blend with the content of the element's parent and the element's background. The blendMode an enumeration defines many possible effects.  

In this example, we are going to use blendMode="difference" .  because we want color should be inverted according to the given color. We want to see red color on progress . so we need to find out inverse of red.

Color inverter online - Find opposite color of spectrum
Free online tool to invert the color code online along with preview. Tool should accept the hex code from user and find the inverted color of hex. Preview of inverted color is displayed along with color code, cmyk, RGB and its decimal values.

I used this tool to get inverse of color. then we give this color our <Rect> and <Text> color.  and then you will see Text color will change when progress on it.

React Native Skia Blend Mode demo
React Native Skia Blend Mode demo