value.interpolate() maps an input range onto an output range, linearly between the points you list, and returns a node usable anywhere a value goes. Outputs may be numbers, colors or strings with units such as '90deg'. Beyond the input range the mapping continues (extrapolate: 'extend', the default); 'clamp' holds the end value. A collapsing header derives four styles from one scroll offset:
const scrollY = useAnimatedValue(0);
const height = scrollY.interpolate({ inputRange: [0, 120], outputRange: [180, 60],
extrapolate: 'clamp' }); // stays within 60..180
const fade = scrollY.interpolate({ inputRange: [0, 60, 120], outputRange: [1, 1, 0] });
const tilt = scrollY.interpolate({ inputRange: [0, 120], outputRange: ['0deg', '90deg'] });
const tint = scrollY.interpolate({ inputRange: [0, 120],
outputRange: ['#1565c0', '#0b1320'] });
// ...listeners copy height, opacity, rotation and color into `row` as h, o, r, c; then:
for (const y of [-40, 0, 60, 90, 120, 200]) {
scrollY.setValue(y); // JS-driven: listeners fire at once
console.log(`y=${y}`.padEnd(6), JSON.stringify(row));
}Output
LOG y=-40 {"h":180,"o":1,"c":"rgba(24, 128, 245, 1)","r":"-30deg"}
LOG y=0 {"h":180,"o":1,"c":"rgba(21, 101, 192, 1)","r":"0deg"}
LOG y=60 {"h":120,"o":1,"c":"rgba(16, 60, 112, 1)","r":"45deg"}
LOG y=90 {"h":90,"o":0.5,"c":"rgba(14, 40, 72, 1)","r":"67.5deg"}
LOG y=120 {"h":60,"o":0,"c":"rgba(11, 19, 32, 1)","r":"90deg"}
LOG y=200 {"h":60,"o":-1.3333333333333335,"c":"rgba(4, -36, -75, 1)","r":"150deg"}The clamped height stops at 60, but at y=200 the unclamped outputs run off the end: opacity -1.33, a color with negative channels, a 150-degree tilt. Overscroll produces exactly such inputs, so clamp every output with a natural limit.