0
# Utility Functions
1
2
Mathematical and interpolation utilities for animation calculations, including value mixing, clamping, geometric operations, and type conversion. These functions provide the building blocks for complex animations and interactions.
3
4
## Capabilities
5
6
### Interpolation Functions
7
8
Core interpolation utilities for mapping values between ranges and creating smooth transitions.
9
10
```typescript { .api }
11
/**
12
* Creates a function that maps input range to output range
13
* @param input - Array of input values (must be ascending)
14
* @param output - Array of output values (same length as input)
15
* @param options - Interpolation configuration
16
* @returns Function that interpolates between the ranges
17
*/
18
function interpolate<T>(
19
input: number[],
20
output: T[],
21
options?: InterpolateOptions<T>
22
): (v: number) => T;
23
24
interface InterpolateOptions<T> {
25
/** Whether to clamp output to range bounds (default: true) */
26
clamp?: boolean;
27
/** Easing function(s) to apply to each segment */
28
ease?: Easing | Easing[];
29
/** Custom mixer function for complex types */
30
mixer?: MixerFactory<T>;
31
}
32
33
type MixerFactory<T> = (from: T, to: T) => (v: number) => T;
34
35
/**
36
* Interpolates between two numbers
37
* @param from - Starting value
38
* @param to - Ending value
39
* @param progress - Interpolation progress (0-1)
40
* @returns Interpolated value
41
*/
42
function mix(from: number, to: number, progress: number): number;
43
```
44
45
**Usage Examples:**
46
47
```typescript
48
import { interpolate, mix } from "popmotion";
49
50
// Basic number interpolation
51
const mixNumbers = interpolate([0, 100], [0, 1]);
52
console.log(mixNumbers(50)); // 0.5
53
54
// Color interpolation
55
const mixColors = interpolate(
56
[0, 50, 100],
57
["#ff0000", "#00ff00", "#0000ff"]
58
);
59
console.log(mixColors(25)); // Blended red-green color
60
61
// Complex string interpolation
62
const mixTransforms = interpolate(
63
[0, 100],
64
["translateX(0px) scale(1)", "translateX(100px) scale(1.5)"]
65
);
66
console.log(mixTransforms(50)); // "translateX(50px) scale(1.25)"
67
68
// With custom easing per segment
69
const easedInterpolate = interpolate(
70
[0, 50, 100],
71
[0, 80, 100],
72
{ ease: ["easeOut", "easeIn"] }
73
);
74
75
// Simple number mixing
76
const currentValue = mix(0, 100, 0.75); // 75
77
```
78
79
### Color Mixing Functions
80
81
Specialized functions for interpolating between colors in different color spaces.
82
83
```typescript { .api }
84
/**
85
* Creates a function to interpolate between two colors
86
* @param from - Starting color (hex, rgb, hsl, or color object)
87
* @param to - Ending color (same format as from)
88
* @returns Function that interpolates between colors
89
*/
90
function mixColor(from: Color | string, to: Color | string): (v: number) => string;
91
92
/**
93
* Interpolates between complex strings containing numbers
94
* @param from - Starting string with numerical values
95
* @param to - Ending string with same structure
96
* @returns Function that interpolates the numerical parts
97
*/
98
function mixComplex(from: string, to: string): (v: number) => string;
99
100
type Color = {
101
red: number;
102
green: number;
103
blue: number;
104
alpha?: number;
105
} | {
106
hue: number;
107
saturation: number;
108
lightness: number;
109
alpha?: number;
110
};
111
```
112
113
**Usage Examples:**
114
115
```typescript
116
import { mixColor, mixComplex } from "popmotion";
117
118
// Color mixing
119
const colorMixer = mixColor("#ff0000", "#0000ff");
120
element.style.backgroundColor = colorMixer(0.5); // Purple blend
121
122
// RGB to HSL mixing
123
const rgbToHsl = mixColor("rgb(255, 0, 0)", "hsl(240, 100%, 50%)");
124
125
// Complex string mixing (CSS transforms, etc.)
126
const transformMixer = mixComplex(
127
"translateX(0px) rotate(0deg) scale(1)",
128
"translateX(100px) rotate(180deg) scale(1.5)"
129
);
130
element.style.transform = transformMixer(0.5);
131
132
// Shadow mixing
133
const shadowMixer = mixComplex(
134
"0px 0px 0px rgba(0,0,0,0)",
135
"10px 10px 20px rgba(0,0,0,0.5)"
136
);
137
element.style.boxShadow = shadowMixer(progress);
138
```
139
140
### Mathematical Utilities
141
142
Core mathematical functions for value manipulation and calculations.
143
144
```typescript { .api }
145
/**
146
* Constrains a value between minimum and maximum bounds
147
* @param min - Minimum value
148
* @param max - Maximum value
149
* @param v - Value to constrain
150
* @returns Clamped value
151
*/
152
function clamp(min: number, max: number, v: number): number;
153
154
/**
155
* Wraps a value around a range (circular behavior)
156
* @param min - Minimum value of range
157
* @param max - Maximum value of range
158
* @param v - Value to wrap
159
* @returns Wrapped value
160
*/
161
function wrap(min: number, max: number, v: number): number;
162
163
/**
164
* Calculates progress of a value within a range
165
* @param from - Start of range
166
* @param to - End of range
167
* @param value - Current value
168
* @returns Progress as 0-1 (can exceed bounds)
169
*/
170
function progress(from: number, to: number, value: number): number;
171
172
/**
173
* Creates a function that snaps values to specific points
174
* @param points - Snap points (number for interval, array for specific points)
175
* @returns Function that snaps input to nearest point
176
*/
177
function snap(points: number | number[]): (v: number) => number;
178
```
179
180
**Usage Examples:**
181
182
```typescript
183
import { clamp, wrap, progress, snap } from "popmotion";
184
185
// Clamp values to valid range
186
const opacity = clamp(0, 1, userInput); // Ensures 0-1 range
187
const angle = clamp(-180, 180, rotation);
188
189
// Wrap for circular behavior
190
const wrappedAngle = wrap(0, 360, 450); // Returns 90
191
const scrollPosition = wrap(0, maxScroll, currentScroll);
192
193
// Calculate progress
194
const scrollProgress = progress(0, 1000, scrollY); // 0.5 at 500px
195
const animationProgress = progress(startTime, endTime, currentTime);
196
197
// Snap to grid or specific values
198
const gridSnap = snap(25); // Snaps to multiples of 25
199
const specificSnap = snap([0, 50, 100, 150]); // Snaps to specific values
200
201
console.log(gridSnap(37)); // 25
202
console.log(gridSnap(63)); // 50
203
console.log(specificSnap(75)); // 50
204
```
205
206
### Geometric Utilities
207
208
Functions for geometric calculations including angles, distances, and coordinate transformations.
209
210
```typescript { .api }
211
/**
212
* Calculates angle between two points in degrees
213
* @param a - First point or origin if b not provided
214
* @param b - Second point (optional)
215
* @returns Angle in degrees
216
*/
217
function angle(a: Point, b?: Point): number;
218
219
/**
220
* Calculates distance between two points or values
221
* @param a - First point or number
222
* @param b - Second point or number
223
* @returns Distance between points
224
*/
225
function distance<P extends Point | number>(a: P, b: P): number;
226
227
/**
228
* Converts angle and distance to x,y coordinates
229
* @param angle - Angle in degrees
230
* @param distance - Distance from origin
231
* @returns Point with x,y coordinates
232
*/
233
function pointFromVector(angle: number, distance: number): Point2D;
234
235
type Point2D = {
236
x: number;
237
y: number;
238
};
239
240
type Point3D = Point2D & {
241
z: number;
242
};
243
244
type Point = Point2D | Point3D;
245
```
246
247
**Usage Examples:**
248
249
```typescript
250
import { angle, distance, pointFromVector } from "popmotion";
251
252
// Calculate angle between mouse and element
253
const mouseAngle = angle(
254
{ x: mouseX, y: mouseY },
255
{ x: elementX, y: elementY }
256
);
257
258
// Distance calculations
259
const dist2D = distance({ x: 0, y: 0 }, { x: 3, y: 4 }); // 5
260
const dist1D = distance(10, 50); // 40
261
const dist3D = distance(
262
{ x: 0, y: 0, z: 0 },
263
{ x: 1, y: 1, z: 1 }
264
); // ~1.73
265
266
// Convert polar to cartesian coordinates
267
const point = pointFromVector(45, 100); // { x: 70.7, y: 70.7 }
268
269
// Rotate point around origin
270
const rotatedPoint = pointFromVector(
271
angle({ x: pointX, y: pointY }) + rotationDelta,
272
distance({ x: 0, y: 0 }, { x: pointX, y: pointY })
273
);
274
```
275
276
### Physics and Animation Helpers
277
278
Utilities for physics calculations and animation smoothing.
279
280
```typescript { .api }
281
/**
282
* Converts per-frame velocity to per-second velocity
283
* @param velocity - Velocity per frame
284
* @param frameDuration - Duration of one frame in milliseconds
285
* @returns Velocity per second
286
*/
287
function velocityPerSecond(velocity: number, frameDuration: number): number;
288
289
/**
290
* Creates function to convert per-second velocity to per-frame
291
* @param fps - Frames per second
292
* @returns Function that converts velocity
293
*/
294
function velocityPerFrame(fps: number): (velocity: number) => number;
295
296
/**
297
* Creates smoothing function for reducing jitter
298
* @param strength - Smoothing strength (0-1, higher = more smoothing)
299
* @returns Function that smooths input values
300
*/
301
function smooth(strength?: number): (v: number) => number;
302
303
/**
304
* Single-frame smoothing calculation
305
* @param previousValue - Previous smoothed value
306
* @param targetValue - New target value
307
* @param delta - Time delta in milliseconds
308
* @param strength - Smoothing strength
309
* @returns New smoothed value
310
*/
311
function smoothFrame(
312
previousValue: number,
313
targetValue: number,
314
delta: number,
315
strength: number
316
): number;
317
```
318
319
**Usage Examples:**
320
321
```typescript
322
import { velocityPerSecond, velocityPerFrame, smooth, smoothFrame } from "popmotion";
323
324
// Velocity conversions
325
const frameVel = 5; // pixels per frame
326
const secVel = velocityPerSecond(frameVel, 16.67); // ~300 pixels/second
327
328
const perFrameConverter = velocityPerFrame(60);
329
const frameVelocity = perFrameConverter(300); // 5 pixels/frame
330
331
// Smooth jittery input (like mouse movement)
332
const smoother = smooth(0.1);
333
let smoothedX = 0;
334
335
document.addEventListener('mousemove', (e) => {
336
smoothedX = smoother(e.clientX);
337
element.style.left = smoothedX + 'px';
338
});
339
340
// Manual smoothing with time delta
341
let lastValue = 0;
342
let lastTime = Date.now();
343
344
function updateSmoothed(newValue) {
345
const now = Date.now();
346
const delta = now - lastTime;
347
348
lastValue = smoothFrame(lastValue, newValue, delta, 0.1);
349
lastTime = now;
350
351
return lastValue;
352
}
353
```
354
355
### Type Checking Utilities
356
357
Type guard functions for runtime type checking of geometric objects.
358
359
```typescript { .api }
360
/**
361
* Type guard to check if object is a Point
362
* @param point - Object to check
363
* @returns True if object has x and y properties
364
*/
365
function isPoint(point: unknown): point is Point;
366
367
/**
368
* Type guard to check if Point is 3D
369
* @param point - Point to check
370
* @returns True if point has z property
371
*/
372
function isPoint3D(point: Point): point is Point3D;
373
```
374
375
**Usage Examples:**
376
377
```typescript
378
import { isPoint, isPoint3D } from "popmotion";
379
380
function handleInput(input: unknown) {
381
if (isPoint(input)) {
382
console.log(`2D point: ${input.x}, ${input.y}`);
383
384
if (isPoint3D(input)) {
385
console.log(`3D point: ${input.x}, ${input.y}, ${input.z}`);
386
}
387
}
388
}
389
390
// Safe point operations
391
function safeDistance(a: unknown, b: unknown) {
392
if (isPoint(a) && isPoint(b)) {
393
return distance(a, b);
394
}
395
return 0;
396
}
397
```
398
399
### Conversion Utilities
400
401
Functions for converting between different units and formats.
402
403
```typescript { .api }
404
/**
405
* Converts degrees to radians
406
* @param degrees - Angle in degrees
407
* @returns Angle in radians
408
*/
409
function degreesToRadians(degrees: number): number;
410
411
/**
412
* Converts radians to degrees
413
* @param radians - Angle in radians
414
* @returns Angle in degrees
415
*/
416
function radiansToDegrees(radians: number): number;
417
418
/**
419
* Converts percentage string to decimal
420
* @param num - Percentage as number (e.g., 50 for "50%")
421
* @returns Decimal value (e.g., 0.5)
422
*/
423
function toDecimal(num: number): number;
424
425
/**
426
* Creates function that applies offset transformation
427
* @param from - Starting offset value
428
* @param to - Ending offset value (optional)
429
* @returns Function that applies offset
430
*/
431
function applyOffset(from: number, to?: number): (v: number) => number;
432
```
433
434
**Usage Examples:**
435
436
```typescript
437
import { degreesToRadians, radiansToDegrees, toDecimal, applyOffset } from "popmotion";
438
439
// Angle conversions
440
const radians = degreesToRadians(90); // π/2
441
const degrees = radiansToDegrees(Math.PI); // 180
442
443
// Percentage conversion
444
const decimal = toDecimal(75); // 0.75
445
446
// Offset transformations
447
const offsetter = applyOffset(10, 110); // Maps 0-1 to 10-110
448
const mappedValue = offsetter(0.5); // 60
449
```
450
451
### Attract Functions
452
453
Specialized utilities for creating attraction effects that pull values towards target points with customizable curves.
454
455
```typescript { .api }
456
/**
457
* Creates a customizable attractor function with displacement modification
458
* @param alterDisplacement - Function to modify displacement calculation (default: identity)
459
* @returns Attractor function that applies the modified displacement
460
*/
461
function createAttractor(alterDisplacement?: (displacement: number) => number):
462
(constant: number, origin: number, v: number) => number;
463
464
/**
465
* Linear attractor function that pulls values towards an origin point
466
* @param constant - Attraction strength (0-1, where 0 = no attraction, 1 = full)
467
* @param origin - Target point to attract towards
468
* @param v - Current value being attracted
469
* @returns New attracted value
470
*/
471
function attract(constant: number, origin: number, v: number): number;
472
473
/**
474
* Exponential attractor function with softer attraction curve
475
* @param constant - Attraction strength (0-1)
476
* @param origin - Target point to attract towards
477
* @param v - Current value being attracted
478
* @returns New attracted value with exponential curve
479
*/
480
function attractExpo(constant: number, origin: number, v: number): number;
481
```
482
483
**Usage Examples:**
484
485
```typescript
486
import { attract, attractExpo, createAttractor } from "popmotion";
487
488
// Linear attraction - pulls value towards origin
489
const result1 = attract(0.5, 10, 5); // Returns 7.5 (halfway to origin)
490
const result2 = attract(1.0, 10, 5); // Returns 10 (full attraction)
491
const result3 = attract(0.0, 10, 5); // Returns 5 (no attraction)
492
493
// Exponential attraction - softer curve
494
const softer1 = attractExpo(0.5, 10, 5); // Returns ~8.9 (softer than linear)
495
const softer2 = attractExpo(0.5, 10, 15); // Returns ~11.1
496
497
// Custom attractor with square root displacement
498
const customAttractor = createAttractor(Math.sqrt);
499
const custom = customAttractor(0.5, 10, 5); // Returns ~8.9
500
501
// Use in animations for magnetic effects
502
animate({
503
from: currentValue,
504
to: targetValue,
505
onUpdate: (value) => {
506
const attracted = attract(0.1, magnetPoint, value);
507
element.style.left = attracted + 'px';
508
}
509
});
510
```
511
512
### Functional Utilities
513
514
Higher-order functions for composing and chaining operations.
515
516
```typescript { .api }
517
/**
518
* Composes functions to run left to right
519
* @param transformers - Functions to compose
520
* @returns Single function that applies all transformers in sequence
521
*/
522
function pipe(...transformers: Array<(v: any) => any>): (v: any) => any;
523
```
524
525
**Usage Examples:**
526
527
```typescript
528
import { pipe, clamp, snap, mix } from "popmotion";
529
530
// Compose multiple transformations
531
const processValue = pipe(
532
(v: number) => clamp(0, 100, v), // First clamp to 0-100
533
snap(10), // Then snap to multiples of 10
534
(v: number) => mix(0, 1, v / 100) // Finally convert to 0-1 range
535
);
536
537
const result = processValue(87.3); // Clamps to 87.3, snaps to 90, converts to 0.9
538
539
// Create reusable transformation chains
540
const normalizeAndSmooth = pipe(
541
(v: number) => clamp(-1, 1, v),
542
(v: number) => (v + 1) / 2, // Convert -1,1 to 0,1
543
smooth(0.1)
544
);
545
```