0
# Interpolation and Easing
1
2
Mathematical functions for smooth value transitions, custom animation curves, and color interpolation that enable sophisticated animation effects.
3
4
## Capabilities
5
6
### Numeric Interpolation
7
8
Maps values from one range to another using linear interpolation.
9
10
```typescript { .api }
11
/**
12
* Maps a value from an input range to an output range using linear interpolation
13
* @param value - The input value to interpolate
14
* @param inputRange - Array of input values (must be monotonically increasing)
15
* @param outputRange - Array of corresponding output values
16
* @param extrapolate - How to handle values outside the input range
17
* @returns Interpolated output value
18
*/
19
function interpolate(
20
value: number,
21
inputRange: readonly number[],
22
outputRange: readonly number[],
23
extrapolate?: ExtrapolationType
24
): number;
25
26
/**
27
* Clamps a value between minimum and maximum bounds
28
* @param value - Value to clamp
29
* @param min - Minimum allowed value
30
* @param max - Maximum allowed value
31
* @returns Clamped value
32
*/
33
function clamp(value: number, min: number, max: number): number;
34
35
enum Extrapolation {
36
/** Extend the interpolation beyond the ranges */
37
EXTEND = "extend",
38
/** Clamp to the edge values of the ranges */
39
CLAMP = "clamp",
40
/** Return the input value as-is when outside ranges */
41
IDENTITY = "identity"
42
}
43
44
type ExtrapolationType = Extrapolation;
45
46
/** @deprecated Use Extrapolation instead */
47
const Extrapolate = Extrapolation;
48
```
49
50
**Usage Examples:**
51
52
```typescript
53
import React from "react";
54
import Animated, {
55
useSharedValue,
56
useAnimatedStyle,
57
interpolate,
58
Extrapolation,
59
clamp,
60
withTiming,
61
useDerivedValue
62
} from "react-native-reanimated";
63
import { Button } from "react-native";
64
65
const InterpolationExample = () => {
66
const progress = useSharedValue(0);
67
68
// Basic interpolation
69
const animatedStyle = useAnimatedStyle(() => ({
70
opacity: interpolate(progress.value, [0, 1], [0.2, 1]),
71
transform: [
72
{
73
scale: interpolate(progress.value, [0, 0.5, 1], [1, 1.5, 1]),
74
},
75
{
76
rotate: `${interpolate(progress.value, [0, 1], [0, 360])}deg`,
77
},
78
],
79
}));
80
81
// Interpolation with different extrapolation modes
82
const extendedStyle = useAnimatedStyle(() => ({
83
// Extends beyond range
84
translateX: interpolate(
85
progress.value,
86
[0, 1],
87
[0, 100],
88
Extrapolation.EXTEND
89
),
90
// Clamps to range bounds
91
translateY: interpolate(
92
progress.value,
93
[0, 1],
94
[0, 100],
95
Extrapolation.CLAMP
96
),
97
}));
98
99
// Complex multi-point interpolation
100
const complexStyle = useAnimatedStyle(() => ({
101
backgroundColor: progress.value < 0.33 ? "red" :
102
progress.value < 0.66 ? "green" : "blue",
103
borderRadius: interpolate(
104
progress.value,
105
[0, 0.25, 0.5, 0.75, 1],
106
[0, 10, 25, 10, 0]
107
),
108
}));
109
110
// Using clamp utility
111
const clampedValue = useDerivedValue(() =>
112
clamp(progress.value * 150, 50, 100)
113
);
114
115
const clampedStyle = useAnimatedStyle(() => ({
116
width: clampedValue.value,
117
height: clampedValue.value,
118
}));
119
120
const animate = () => {
121
progress.value = withTiming(progress.value === 0 ? 1 : 0, {
122
duration: 2000
123
});
124
};
125
126
return (
127
<>
128
<Button title="Animate" onPress={animate} />
129
130
{/* Basic interpolation */}
131
<Animated.View
132
style={[
133
{ width: 100, height: 100, backgroundColor: "lightblue", margin: 10 },
134
animatedStyle,
135
]}
136
/>
137
138
{/* Extended interpolation */}
139
<Animated.View
140
style={[
141
{ width: 50, height: 50, backgroundColor: "lightgreen", margin: 10 },
142
extendedStyle,
143
]}
144
/>
145
146
{/* Complex interpolation */}
147
<Animated.View
148
style={[
149
{ width: 80, height: 80, margin: 10 },
150
complexStyle,
151
]}
152
/>
153
154
{/* Clamped size */}
155
<Animated.View
156
style={[
157
{ backgroundColor: "lightcoral", margin: 10 },
158
clampedStyle,
159
]}
160
/>
161
</>
162
);
163
};
164
```
165
166
### Color Interpolation
167
168
Interpolates between color values in different color spaces.
169
170
```typescript { .api }
171
/**
172
* Interpolates between color values
173
* @param value - The input value to interpolate
174
* @param inputRange - Array of input values (must be monotonically increasing)
175
* @param outputRange - Array of color strings to interpolate between
176
* @param colorSpace - Color space for interpolation (RGB or HSV)
177
* @param options - Additional interpolation options
178
* @returns Interpolated color string
179
*/
180
function interpolateColor(
181
value: number,
182
inputRange: readonly number[],
183
outputRange: readonly string[],
184
colorSpace?: ColorSpace,
185
options?: InterpolationOptions
186
): string;
187
188
enum ColorSpace {
189
RGB = "rgb",
190
HSV = "hsv"
191
}
192
193
interface InterpolationOptions {
194
/** Gamma correction for RGB interpolation */
195
gamma?: number;
196
/** Whether to use shorter or longer path in HSV */
197
useCorrectedHSVInterpolation?: boolean;
198
}
199
200
/**
201
* Hook for creating reusable color interpolation configurations
202
* @param inputRange - Input range for interpolation
203
* @param outputRange - Output color range
204
* @param colorSpace - Color space to use
205
* @returns Interpolation configuration function
206
*/
207
function useInterpolateConfig(
208
inputRange: readonly number[],
209
outputRange: readonly string[],
210
colorSpace?: ColorSpace
211
): (value: number) => string;
212
```
213
214
**Usage Examples:**
215
216
```typescript
217
import React from "react";
218
import Animated, {
219
useSharedValue,
220
useAnimatedStyle,
221
interpolateColor,
222
ColorSpace,
223
withTiming,
224
useInterpolateConfig,
225
useDerivedValue
226
} from "react-native-reanimated";
227
import { Button } from "react-native";
228
229
const ColorInterpolationExample = () => {
230
const progress = useSharedValue(0);
231
232
// RGB color interpolation
233
const rgbStyle = useAnimatedStyle(() => ({
234
backgroundColor: interpolateColor(
235
progress.value,
236
[0, 0.5, 1],
237
["#FF0000", "#00FF00", "#0000FF"], // Red -> Green -> Blue
238
ColorSpace.RGB
239
),
240
}));
241
242
// HSV color interpolation (smoother transitions)
243
const hsvStyle = useAnimatedStyle(() => ({
244
backgroundColor: interpolateColor(
245
progress.value,
246
[0, 1],
247
["#FF0000", "#0000FF"], // Red -> Blue via HSV
248
ColorSpace.HSV
249
),
250
}));
251
252
// Complex color progression
253
const rainbowStyle = useAnimatedStyle(() => ({
254
backgroundColor: interpolateColor(
255
progress.value,
256
[0, 0.16, 0.33, 0.5, 0.66, 0.83, 1],
257
[
258
"#FF0000", // Red
259
"#FF8000", // Orange
260
"#FFFF00", // Yellow
261
"#00FF00", // Green
262
"#0000FF", // Blue
263
"#8000FF", // Indigo
264
"#FF00FF", // Violet
265
],
266
ColorSpace.HSV
267
),
268
}));
269
270
// Using interpolation config hook
271
const colorConfig = useInterpolateConfig(
272
[0, 0.3, 0.7, 1],
273
["#1a1a1a", "#ff6b6b", "#4ecdc4", "#45b7d1"],
274
ColorSpace.RGB
275
);
276
277
const configStyle = useAnimatedStyle(() => ({
278
backgroundColor: colorConfig(progress.value),
279
}));
280
281
// Color with alpha interpolation
282
const alphaStyle = useAnimatedStyle(() => ({
283
backgroundColor: interpolateColor(
284
progress.value,
285
[0, 1],
286
["rgba(255, 0, 0, 0.2)", "rgba(255, 0, 0, 1.0)"]
287
),
288
}));
289
290
// Border color animation
291
const borderStyle = useAnimatedStyle(() => ({
292
borderColor: interpolateColor(
293
progress.value,
294
[0, 1],
295
["#cccccc", "#ff0000"],
296
ColorSpace.RGB
297
),
298
borderWidth: 3,
299
}));
300
301
const animate = () => {
302
progress.value = withTiming(progress.value === 0 ? 1 : 0, {
303
duration: 3000
304
});
305
};
306
307
return (
308
<>
309
<Button title="Animate Colors" onPress={animate} />
310
311
{/* RGB interpolation */}
312
<Animated.View
313
style={[
314
{ width: 100, height: 50, margin: 5, borderRadius: 5 },
315
rgbStyle,
316
]}
317
/>
318
319
{/* HSV interpolation */}
320
<Animated.View
321
style={[
322
{ width: 100, height: 50, margin: 5, borderRadius: 5 },
323
hsvStyle,
324
]}
325
/>
326
327
{/* Rainbow progression */}
328
<Animated.View
329
style={[
330
{ width: 100, height: 50, margin: 5, borderRadius: 5 },
331
rainbowStyle,
332
]}
333
/>
334
335
{/* Config-based interpolation */}
336
<Animated.View
337
style={[
338
{ width: 100, height: 50, margin: 5, borderRadius: 5 },
339
configStyle,
340
]}
341
/>
342
343
{/* Alpha interpolation */}
344
<Animated.View
345
style={[
346
{ width: 100, height: 50, margin: 5, borderRadius: 5 },
347
alphaStyle,
348
]}
349
/>
350
351
{/* Border color */}
352
<Animated.View
353
style={[
354
{
355
width: 100,
356
height: 50,
357
margin: 5,
358
borderRadius: 5,
359
backgroundColor: "white"
360
},
361
borderStyle,
362
]}
363
/>
364
</>
365
);
366
};
367
```
368
369
### Color Processing Functions
370
371
Utility functions for color validation, conversion, and processing for animations.
372
373
```typescript { .api }
374
/**
375
* Checks if a value is a valid color string or number
376
* @param value - Value to test for color validity
377
* @returns True if value is a valid color
378
*/
379
function isColor(value: unknown): boolean;
380
381
/**
382
* Converts a color value to RGBA array format
383
* @param color - Color value (string, number, or hex)
384
* @returns RGBA array with values from 0-1
385
*/
386
function convertToRGBA(color: unknown): ParsedColorArray;
387
388
/**
389
* Processes color values for platform-specific rendering
390
* @param color - Raw color value to process
391
* @returns Processed color number, null, or undefined
392
*/
393
function processColor(color: unknown): number | null | undefined;
394
395
type ParsedColorArray = [number, number, number, number]; // [r, g, b, a]
396
```
397
398
**Usage Examples:**
399
400
```typescript
401
import { isColor, convertToRGBA, processColor } from "react-native-reanimated";
402
403
// Color validation
404
const validateColors = () => {
405
console.log(isColor("red")); // true
406
console.log(isColor("#FF0000")); // true
407
console.log(isColor("rgb(255,0,0)")); // true
408
console.log(isColor("invalid")); // false
409
console.log(isColor(123)); // false
410
};
411
412
// Color conversion for animations
413
const useColorAnimation = () => {
414
const colorValue = useSharedValue("red");
415
416
const animatedStyle = useAnimatedStyle(() => {
417
// Convert color to RGBA for processing
418
const [r, g, b, a] = convertToRGBA(colorValue.value);
419
420
return {
421
backgroundColor: `rgba(${r * 255}, ${g * 255}, ${b * 255}, ${a})`,
422
};
423
});
424
425
return animatedStyle;
426
};
427
428
// Color processing for platform compatibility
429
const processColors = () => {
430
const colors = ["red", "#FF0000", "rgb(255,0,0)", null, undefined];
431
432
colors.forEach(color => {
433
const processed = processColor(color);
434
console.log(`${color} -> ${processed}`);
435
});
436
};
437
438
// Advanced color interpolation with conversion
439
const ColorTransition = () => {
440
const progress = useSharedValue(0);
441
442
const animatedStyle = useAnimatedStyle(() => {
443
// Convert start and end colors to RGBA
444
const startColor = convertToRGBA("red"); // [1, 0, 0, 1]
445
const endColor = convertToRGBA("blue"); // [0, 0, 1, 1]
446
447
// Manually interpolate each component
448
const r = interpolate(progress.value, [0, 1], [startColor[0], endColor[0]]);
449
const g = interpolate(progress.value, [0, 1], [startColor[1], endColor[1]]);
450
const b = interpolate(progress.value, [0, 1], [startColor[2], endColor[2]]);
451
const a = interpolate(progress.value, [0, 1], [startColor[3], endColor[3]]);
452
453
return {
454
backgroundColor: `rgba(${r * 255}, ${g * 255}, ${b * 255}, ${a})`,
455
};
456
});
457
458
return <Animated.View style={animatedStyle} />;
459
};
460
```
461
462
### Easing Functions
463
464
Predefined and custom easing functions for natural animation curves.
465
466
```typescript { .api }
467
const Easing: {
468
/** Linear easing function (f(t) = t) */
469
linear: EasingFunction;
470
/** Default ease function with gentle acceleration and deceleration */
471
ease: EasingFunction;
472
/** Quadratic easing (f(t) = t²) */
473
quad: EasingFunction;
474
/** Cubic easing (f(t) = t³) */
475
cubic: EasingFunction;
476
/** Polynomial easing with custom exponent */
477
poly(n: number): EasingFunction;
478
/** Sinusoidal easing */
479
sin: EasingFunction;
480
/** Circular easing */
481
circle: EasingFunction;
482
/** Exponential easing */
483
exp: EasingFunction;
484
/** Elastic spring easing with bounce effect */
485
elastic(bounciness?: number): EasingFunction;
486
/** Back easing with overshoot effect */
487
back(s?: number): EasingFunction;
488
/** Bouncing easing with multiple bounces */
489
bounce: EasingFunction;
490
/** Cubic bezier curve easing */
491
bezier(x1: number, y1: number, x2: number, y2: number): EasingFunction;
492
493
/** Apply easing in forward direction */
494
in(easing: EasingFunction): EasingFunction;
495
/** Apply easing in reverse direction */
496
out(easing: EasingFunction): EasingFunction;
497
/** Apply easing in both directions (symmetric) */
498
inOut(easing: EasingFunction): EasingFunction;
499
};
500
501
type EasingFunction = (value: number) => number;
502
503
interface EasingFunctionFactory {
504
factory(): EasingFunction;
505
}
506
```
507
508
**Usage Examples:**
509
510
```typescript
511
import React from "react";
512
import Animated, {
513
useSharedValue,
514
useAnimatedStyle,
515
withTiming,
516
Easing
517
} from "react-native-reanimated";
518
import { Button } from "react-native";
519
520
const EasingExample = () => {
521
const progress1 = useSharedValue(0);
522
const progress2 = useSharedValue(0);
523
const progress3 = useSharedValue(0);
524
const progress4 = useSharedValue(0);
525
526
// Linear easing
527
const linearStyle = useAnimatedStyle(() => ({
528
transform: [{ translateX: progress1.value * 200 }],
529
}));
530
531
// Ease out quadratic
532
const easeOutStyle = useAnimatedStyle(() => ({
533
transform: [{ translateX: progress2.value * 200 }],
534
}));
535
536
// Bounce easing
537
const bounceStyle = useAnimatedStyle(() => ({
538
transform: [{ translateX: progress3.value * 200 }],
539
}));
540
541
// Custom bezier easing
542
const bezierStyle = useAnimatedStyle(() => ({
543
transform: [{ translateX: progress4.value * 200 }],
544
}));
545
546
const animateLinear = () => {
547
progress1.value = withTiming(progress1.value === 0 ? 1 : 0, {
548
duration: 1000,
549
easing: Easing.linear,
550
});
551
};
552
553
const animateEaseOut = () => {
554
progress2.value = withTiming(progress2.value === 0 ? 1 : 0, {
555
duration: 1000,
556
easing: Easing.out(Easing.quad),
557
});
558
};
559
560
const animateBounce = () => {
561
progress3.value = withTiming(progress3.value === 0 ? 1 : 0, {
562
duration: 1500,
563
easing: Easing.bounce,
564
});
565
};
566
567
const animateBezier = () => {
568
progress4.value = withTiming(progress4.value === 0 ? 1 : 0, {
569
duration: 1000,
570
easing: Easing.bezier(0.25, 0.1, 0.25, 1), // CSS ease
571
});
572
};
573
574
return (
575
<>
576
<Button title="Linear" onPress={animateLinear} />
577
<Animated.View
578
style={[
579
{ width: 50, height: 50, backgroundColor: "red", margin: 5 },
580
linearStyle,
581
]}
582
/>
583
584
<Button title="Ease Out Quad" onPress={animateEaseOut} />
585
<Animated.View
586
style={[
587
{ width: 50, height: 50, backgroundColor: "green", margin: 5 },
588
easeOutStyle,
589
]}
590
/>
591
592
<Button title="Bounce" onPress={animateBounce} />
593
<Animated.View
594
style={[
595
{ width: 50, height: 50, backgroundColor: "blue", margin: 5 },
596
bounceStyle,
597
]}
598
/>
599
600
<Button title="Bezier" onPress={animateBezier} />
601
<Animated.View
602
style={[
603
{ width: 50, height: 50, backgroundColor: "purple", margin: 5 },
604
bezierStyle,
605
]}
606
/>
607
</>
608
);
609
};
610
```
611
612
### Advanced Easing Patterns
613
614
Complex easing combinations and custom curves.
615
616
**Custom Easing Functions:**
617
618
```typescript
619
import { Easing } from "react-native-reanimated";
620
621
// Create custom easing functions
622
const customEasings = {
623
// Elastic with custom bounciness
624
elasticOut: Easing.out(Easing.elastic(2)),
625
626
// Back with overshoot
627
backInOut: Easing.inOut(Easing.back(1.7)),
628
629
// Custom polynomial
630
fastSlowFast: Easing.inOut(Easing.poly(3)),
631
632
// Complex bezier curves
633
cssEase: Easing.bezier(0.25, 0.1, 0.25, 1),
634
cssEaseIn: Easing.bezier(0.42, 0, 1, 1),
635
cssEaseOut: Easing.bezier(0, 0, 0.58, 1),
636
cssEaseInOut: Easing.bezier(0.42, 0, 0.58, 1),
637
638
// Material Design curves
639
materialStandard: Easing.bezier(0.4, 0.0, 0.2, 1),
640
materialDecelerate: Easing.bezier(0.0, 0.0, 0.2, 1),
641
materialAccelerate: Easing.bezier(0.4, 0.0, 1, 1),
642
643
// Custom stepped function
644
stepped: (steps: number) => (t: number) => Math.floor(t * steps) / steps,
645
};
646
```
647
648
### Interpolation Configuration
649
650
Advanced interpolation patterns and utilities.
651
652
```typescript { .api }
653
interface InterpolateConfig {
654
inputRange: readonly number[];
655
outputRange: readonly (string | number)[];
656
extrapolate?: ExtrapolationType;
657
extrapolateLeft?: ExtrapolationType;
658
extrapolateRight?: ExtrapolationType;
659
}
660
661
interface InterpolateHSV {
662
h: number;
663
s: number;
664
v: number;
665
}
666
667
interface InterpolateRGB {
668
r: number;
669
g: number;
670
b: number;
671
}
672
```
673
674
**Advanced Interpolation Example:**
675
676
```typescript
677
import React from "react";
678
import Animated, {
679
useSharedValue,
680
useAnimatedStyle,
681
interpolate,
682
Extrapolation,
683
withTiming
684
} from "react-native-reanimated";
685
686
const AdvancedInterpolationExample = () => {
687
const scrollY = useSharedValue(0);
688
689
// Complex header transformation
690
const headerStyle = useAnimatedStyle(() => {
691
const headerHeight = 200;
692
const minHeight = 80;
693
694
return {
695
height: interpolate(
696
scrollY.value,
697
[0, headerHeight - minHeight],
698
[headerHeight, minHeight],
699
Extrapolation.CLAMP
700
),
701
opacity: interpolate(
702
scrollY.value,
703
[0, headerHeight / 2, headerHeight],
704
[1, 0.8, 0.3],
705
Extrapolation.CLAMP
706
),
707
transform: [
708
{
709
scale: interpolate(
710
scrollY.value,
711
[0, headerHeight],
712
[1, 0.9],
713
Extrapolation.CLAMP
714
),
715
},
716
],
717
};
718
});
719
720
// Parallax background
721
const backgroundStyle = useAnimatedStyle(() => ({
722
transform: [
723
{
724
translateY: interpolate(
725
scrollY.value,
726
[0, 300],
727
[0, -100],
728
Extrapolation.EXTEND // Allow over-scroll effect
729
),
730
},
731
{
732
scale: interpolate(
733
scrollY.value,
734
[-100, 0, 300],
735
[1.2, 1, 0.8],
736
{
737
extrapolateLeft: Extrapolation.CLAMP,
738
extrapolateRight: Extrapolation.EXTEND,
739
}
740
),
741
},
742
],
743
}));
744
745
return (
746
<>
747
<Animated.View style={[{ position: "absolute", top: 0 }, backgroundStyle]}>
748
{/* Background content */}
749
</Animated.View>
750
751
<Animated.View style={headerStyle}>
752
{/* Header content */}
753
</Animated.View>
754
755
<Animated.ScrollView
756
onScroll={(event) => {
757
scrollY.value = event.nativeEvent.contentOffset.y;
758
}}
759
scrollEventThrottle={16}
760
>
761
{/* Scroll content */}
762
</Animated.ScrollView>
763
</>
764
);
765
};
766
```
767
768
## Type Definitions
769
770
```typescript { .api }
771
type ExtrapolationType = Extrapolation | {
772
extrapolateLeft?: Extrapolation;
773
extrapolateRight?: Extrapolation;
774
};
775
776
interface ExtrapolationConfig {
777
extrapolateLeft?: ExtrapolationType;
778
extrapolateRight?: ExtrapolationType;
779
}
780
781
type ParsedColorArray = readonly [number, number, number, number]; // RGBA
782
783
interface InterpolationOptions {
784
gamma?: number;
785
useCorrectedHSVInterpolation?: boolean;
786
}
787
```