Interpolation functions map input values to output values, enabling smooth transitions and animations. React Native Skia provides utilities for interpolating numbers, colors, vectors, and paths.
interpolate
Maps a value from an input range to an output range.
import { interpolate } from "@shopify/react-native-skia";
// Basic interpolation
const value = interpolate(50, [0, 100], [0, 1]);
console.log(value); // 0.5
// Non-linear mapping
const scaled = interpolate(50, [0, 100], [0, 200]);
console.log(scaled); // 100
Function Signature
function interpolate(
value: number,
inputRange: number[],
outputRange: number[],
options?: ExtrapolationType
): number
Parameters
The input value to interpolate
Array of input values (must be monotonically increasing)
Array of output values (same length as inputRange)
Extrapolation behavior:
"extend": Continues the pattern beyond the range (default)
"clamp": Clamps to the nearest edge value
"identity": Returns the input value unchanged
{ extrapolateLeft: Extrapolate, extrapolateRight: Extrapolate }
Examples
Linear Interpolation
// Map progress (0-1) to position (0-300)
const progress = 0.5;
const position = interpolate(progress, [0, 1], [0, 300]);
// position = 150
Multi-Point Interpolation
// Ease in-out effect
const progress = 0.5;
const eased = interpolate(
progress,
[0, 0.25, 0.75, 1],
[0, 0.1, 0.9, 1]
);
import { Extrapolate } from "@shopify/react-native-skia";
// Clamp to range
const value = interpolate(150, [0, 100], [0, 1], "clamp");
// value = 1 (clamped)
// Extend beyond range
const extended = interpolate(150, [0, 100], [0, 1], "extend");
// extended = 1.5 (extended)
// Different extrapolation on each side
const mixed = interpolate(150, [0, 100], [0, 1], {
extrapolateLeft: Extrapolate.CLAMP,
extrapolateRight: Extrapolate.EXTEND,
});
interpolateColors
Interpolates between colors.
import { interpolateColors } from "@shopify/react-native-skia";
const progress = 0.5;
const color = interpolateColors(
progress,
[0, 1],
["red", "blue"]
);
// Returns an RGBA color array
Function Signature
function interpolateColors(
value: number,
inputRange: number[],
outputRange: Color[]
): SkColor
Examples
Gradient Effect
import { useSharedValue, useDerivedValue } from "react-native-reanimated";
import { Canvas, Circle, interpolateColors } from "@shopify/react-native-skia";
export default function ColorTransition() {
const progress = useSharedValue(0);
useEffect(() => {
progress.value = withRepeat(
withTiming(1, { duration: 2000 }),
-1,
true
);
}, []);
const color = useDerivedValue(() => {
return interpolateColors(
progress.value,
[0, 0.5, 1],
["red", "yellow", "green"]
);
});
return (
<Canvas style={{ flex: 1 }}>
<Circle cx={150} cy={150} r={100} color={color} />
</Canvas>
);
}
mixColors
Mixes two colors by a factor.
import { mixColors } from "@shopify/react-native-skia";
const mixed = mixColors(0.5, "red", "blue");
// Returns a color halfway between red and blue
Function Signature
function mixColors(
value: number,
color1: Color,
color2: Color
): SkColor
interpolatePaths
Interpolates between path shapes.
import { interpolatePaths, Skia } from "@shopify/react-native-skia";
const path1 = Skia.Path.MakeFromSVGString("M 0 0 L 100 0 L 50 100 Z");
const path2 = Skia.Path.MakeFromSVGString("M 0 100 L 100 100 L 50 0 Z");
const morphed = interpolatePaths(0.5, [0, 1], [path1, path2]);
Function Signature
function interpolatePaths(
value: number,
inputRange: number[],
outputRange: SkPath[],
options?: ExtrapolationType,
output?: SkPath
): SkPath
Requirements
- Paths must be interpolatable (same number of points and commands)
- Use
path1.isInterpolatable(path2) to check compatibility
Examples
Morphing Shapes
import { useSharedValue, useDerivedValue, withRepeat, withTiming } from "react-native-reanimated";
import { Canvas, Path, interpolatePaths, Skia } from "@shopify/react-native-skia";
import { useEffect } from "react";
const triangle = Skia.Path.MakeFromSVGString("M 50 0 L 100 100 L 0 100 Z");
const square = Skia.Path.MakeFromSVGString("M 0 0 L 100 0 L 100 100 L 0 100 Z");
export default function MorphingShape() {
const progress = useSharedValue(0);
useEffect(() => {
progress.value = withRepeat(
withTiming(1, { duration: 2000 }),
-1,
true
);
}, []);
const path = useDerivedValue(() => {
return interpolatePaths(
progress.value,
[0, 1],
[triangle, square]
);
});
return (
<Canvas style={{ flex: 1 }}>
<Path path={path} color="purple" />
</Canvas>
);
}
interpolateVector
Interpolates between 2D vectors.
import { interpolateVector } from "@shopify/react-native-skia";
const start = { x: 0, y: 0 };
const end = { x: 100, y: 100 };
const point = interpolateVector(0.5, [0, 1], [start, end]);
// point = { x: 50, y: 50 }
Using with Animations
Animated Position
import { useSharedValue, useDerivedValue } from "react-native-reanimated";
import { Canvas, Circle, interpolate } from "@shopify/react-native-skia";
const progress = useSharedValue(0);
const x = useDerivedValue(() => {
return interpolate(progress.value, [0, 1], [50, 250]);
});
const y = useDerivedValue(() => {
return interpolate(progress.value, [0, 1], [100, 300]);
});
<Circle cx={x} cy={y} r={30} color="blue" />
Animated Scale
const progress = useSharedValue(0);
const scale = useDerivedValue(() => {
return interpolate(
progress.value,
[0, 0.5, 1],
[1, 1.5, 1],
"clamp"
);
});
<Circle
cx={150}
cy={150}
r={() => 50 * scale.value}
color="green"
/>
Animated Rotation
const progress = useSharedValue(0);
const rotation = useDerivedValue(() => {
return interpolate(
progress.value,
[0, 1],
[0, Math.PI * 2]
);
});
<Rect
x={0}
y={0}
width={100}
height={100}
color="red"
origin={{ x: 50, y: 50 }}
transform={[{ rotate: rotation }]}
/>
Easing Functions
Combine with easing for natural motion:
import { Easing } from "react-native-reanimated";
import { interpolate } from "@shopify/react-native-skia";
const progress = useSharedValue(0);
const eased = useDerivedValue(() => {
// Apply easing
const easedProgress = Easing.bezier(0.25, 0.1, 0.25, 1)(progress.value);
// Then interpolate
return interpolate(easedProgress, [0, 1], [0, 300]);
});
Common Patterns
Fade In/Out
const opacity = useDerivedValue(() => {
return interpolate(
progress.value,
[0, 0.3, 0.7, 1],
[0, 1, 1, 0],
"clamp"
);
});
Bounce Effect
const scale = useDerivedValue(() => {
return interpolate(
progress.value,
[0, 0.25, 0.5, 0.75, 1],
[1, 1.2, 0.9, 1.05, 1]
);
});
const scrollY = useSharedValue(0);
const backgroundY = useDerivedValue(() => {
return interpolate(
scrollY.value,
[0, 500],
[0, -100], // Slower movement
"extend"
);
});
const foregroundY = useDerivedValue(() => {
return interpolate(
scrollY.value,
[0, 500],
[0, -200], // Faster movement
"extend"
);
});
- Use
useDerivedValue for interpolation in animations
- Mark interpolation functions as worklets with
"worklet"
- Avoid creating new arrays in interpolate calls
- Reuse output ranges when possible
- For path interpolation, check
isInterpolatable() first
- Use
"clamp" extrapolation to prevent unexpected values
import { Extrapolate } from "@shopify/react-native-skia";
// Clamp to edges
Extrapolate.CLAMP
// Continue the pattern
Extrapolate.EXTEND
// Return input value unchanged
Extrapolate.IDENTITY