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.
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.