In React Native Animated, what does interpolate do, and why can the default extrapolate make a springy heart shrink below its resting size?
answer
- map input ranges to output ranges
- inputRange must not decrease
- strings and colours as outputs
- default extrapolate is extend
- clamp stops the overshoot
basics
~20 sinterpolate maps a value's inputRange onto an outputRange, piecewise and linear by default. Outside the range it extends the edge segment unless told otherwise, so a spring overshooting past 1 pushes the output beyond the last value; extrapolate: 'clamp' fixes that.
solid answer
~40 s`value.interpolate({inputRange, outputRange})` returns a derived animated node that maps the input onto the output piecewise-linearly; `inputRange` must be monotonically non-decreasing and the two arrays must have the same length. Outputs can be numbers, colours or strings with units such as `'45deg'`, and an `easing` can shape each segment. Outside the input range, `extrapolate` decides: `'extend'` (the default) continues the edge segment's slope, `'clamp'` holds the edge output, and `'identity'` returns the input itself; `extrapolateLeft` and `extrapolateRight` set each side. That default bites with springs: map a progress value with `inputRange: [0, 0.5, 1]` to scale `[1, 1.4, 1]`, spring the progress to 1, and the overshoot to 1.1 extends the falling last segment, so the heart dips to 0.92 before settling. Adding `extrapolate: 'clamp'` pins it at 1. Interpolation works with the native driver.
code
tsx · 23 linesimport {Animated, useAnimatedValue} from 'react-native';
export function useHeartPop() {
const progress = useAnimatedValue(0);
const scale = progress.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [1, 1.4, 1],
extrapolate: 'clamp',
});
const rotate = progress.interpolate({
inputRange: [0, 0.5, 1],
outputRange: ['0deg', '-12deg', '0deg'],
extrapolate: 'clamp',
});
const play = () => {
progress.setValue(0);
Animated.spring(progress, {toValue: 1, friction: 4, useNativeDriver: true}).start();
};
return {play, style: {transform: [{scale}, {rotate}]}};
}go deeper
Recall that interpolate maps an input range to an output range, and that outputs can be numbers, colours or degree strings.
Explain the three extrapolate modes, the default of extend, and the rules on range order and length.
Predict and prevent overshoot artefacts from springs and decay, and drive several properties from one progress value.
Decide how shared motion tokens, such as a standard pop curve, are expressed so interpolation mistakes are not repeated across screens.
## What interpolate is `interpolate` creates a **derived value**: an animated node whose output is computed from another animated value. You describe the mapping with two arrays: - `inputRange` — the input values, which must be **monotonically non-decreasing** and contain at least two entries; - `outputRange` — the matching outputs, **the same length** as `inputRange`. Between two neighbouring input points the mapping is linear by default, and an `easing` function can shape it. Because the derived node is part of the animation graph, it works with `useNativeDriver: true` as long as the style it feeds is native-driveable. ## What the output can be - **Numbers**, for `scale`, `opacity`, `translateX` and so on. - **Colours**, such as `'rgb(200,200,200)'` to `'rgb(224,36,94)'`, for `backgroundColor` or `color`. - **Strings with units**, such as `'0deg'` to `'20deg'` for `rotate`. All output strings must have the same shape. One animated progress value from 0 to 1 can therefore drive the heart's scale, colour and tilt at once, each through its own `interpolate`. ## Extrapolation: what happens outside the range | Mode | Behaviour outside `inputRange` | |---|---| | `'extend'` (default) | continues the slope of the nearest edge segment | | `'clamp'` | holds the edge output value | | `'identity'` | returns the input value unchanged | `extrapolate` sets both sides; `extrapolateLeft` and `extrapolateRight` override one side each. ## Why the default surprises people with springs Springs overshoot their target. Suppose a progress value springs from 0 to 1, and the heart's scale is: - `inputRange: [0, 0.5, 1]` - `outputRange: [1, 1.4, 1]` The last segment falls from 1.4 at 0.5 to 1 at 1, a slope of −0.8 per unit. When the spring overshoots to 1.1, `'extend'` continues that line: 1.4 + (0.6 / 0.5) × (1 − 1.4) = **0.92**. The heart briefly shrinks below its resting size, and on an opacity mapping the same effect can produce values below 0 or above 1. With `extrapolate: 'clamp'`, any input above 1 maps to 1, so the heart settles cleanly. ## When extend is what you want - Mapping a scroll offset to a parallax translation that should keep moving beyond the range. - Converting units, such as degrees derived from a value that may run past its nominal range. ## Common mistakes 1. A decreasing `inputRange` such as `[1, 0]`, which fails the "monotonically non-decreasing" check; reverse the `outputRange` instead. 2. Arrays of different lengths. 3. Forgetting `clamp` on progress values driven by springs or `decay`. 4. Mixing number and string outputs in one `outputRange`. ## Interpolating from other animated nodes - An interpolation is itself an animated node, so it has an `interpolate` method too; you can map a progress value to a scale, then that scale to a shadow opacity. - Several interpolations can read the same source value, which keeps a multi-property effect in sync: the heart's scale, tilt and colour all follow one `progress`. - `Animated.add`, `Animated.multiply` and similar helpers combine values before interpolating, for example adding a base offset to a gesture delta. ## A worked design for the heart 1. Drive a single `progress` from 0 to 1 with a spring. 2. Interpolate `scale` with `[0, 0.5, 1] → [1, 1.4, 1]` and `extrapolate: 'clamp'`. 3. Interpolate `rotate` with `[0, 0.5, 1] → ['0deg', '-12deg', '0deg']`, also clamped. 4. Interpolate the colour from grey to red with `[0, 1]`; clamping keeps a spring's overshoot from producing an invalid colour. 5. Start everything with `useNativeDriver: true`, since transforms and colours are both native-driveable. The result is one spring, one value and three properties that can never drift apart.
- In React Native Animated, how do you run an interpolation backwards when inputRange must be non-decreasing?Keep `inputRange` ascending and reverse the `outputRange`. For a value that goes from 1 to 0 while opacity should go from 0 to 1, write `inputRange: [0, 1]` and `outputRange: [1, 0]`. A descending `inputRange` such as `[1, 0]` fails the non-decreasing check.
- What do extrapolateLeft and extrapolateRight add over extrapolate in React Native Animated?They set the behaviour on one side of the input range, overriding `extrapolate` for that side. A common case is clamping only the right side so a spring's overshoot cannot push past the final value, while the left side keeps extending for inputs below the range.
saying these in an interview costs you the question
- interpolate clamps to the output range by default
- inputRange can be in any order as long as outputRange matches
- interpolate cannot be used with useNativeDriver: true
- outputRange can only contain numbers, not colours or degrees
- A spring's overshoot never affects interpolated values