@codellyson/framely 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/package.json +3 -2
  2. package/src/AbsoluteFill.tsx +50 -0
  3. package/src/Audio.tsx +294 -0
  4. package/src/Composition.tsx +378 -0
  5. package/src/Easing.ts +294 -0
  6. package/src/ErrorBoundary.tsx +136 -0
  7. package/src/Folder.tsx +66 -0
  8. package/src/Freeze.tsx +63 -0
  9. package/src/IFrame.tsx +100 -0
  10. package/src/Img.tsx +146 -0
  11. package/src/Loop.tsx +139 -0
  12. package/src/Player.tsx +594 -0
  13. package/src/Sequence.tsx +80 -0
  14. package/src/Series.tsx +181 -0
  15. package/src/Text.tsx +376 -0
  16. package/src/Video.tsx +247 -0
  17. package/src/__tests__/Easing.test.js +119 -0
  18. package/src/__tests__/interpolate.test.js +127 -0
  19. package/src/config.ts +406 -0
  20. package/src/context.tsx +241 -0
  21. package/src/delayRender.ts +278 -0
  22. package/src/getInputProps.ts +217 -0
  23. package/src/hooks/useDelayRender.ts +117 -0
  24. package/src/hooks.ts +28 -0
  25. package/src/index.d.ts +571 -0
  26. package/src/index.ts +260 -0
  27. package/src/interpolate.ts +160 -0
  28. package/src/interpolateColors.ts +368 -0
  29. package/src/makeTransform.ts +339 -0
  30. package/src/measureSpring.ts +152 -0
  31. package/src/noise.ts +308 -0
  32. package/src/preload.ts +303 -0
  33. package/src/registerRoot.ts +346 -0
  34. package/src/shapes/Circle.tsx +37 -0
  35. package/src/shapes/Ellipse.tsx +39 -0
  36. package/src/shapes/Line.tsx +37 -0
  37. package/src/shapes/Path.tsx +56 -0
  38. package/src/shapes/Polygon.tsx +39 -0
  39. package/src/shapes/Rect.tsx +43 -0
  40. package/src/shapes/Svg.tsx +39 -0
  41. package/src/shapes/index.ts +16 -0
  42. package/src/shapes/usePathLength.ts +38 -0
  43. package/src/staticFile.ts +117 -0
  44. package/src/templates/api.ts +165 -0
  45. package/src/templates/index.ts +7 -0
  46. package/src/templates/mockData.ts +271 -0
  47. package/src/templates/types.ts +126 -0
  48. package/src/transitions/TransitionSeries.tsx +399 -0
  49. package/src/transitions/index.ts +109 -0
  50. package/src/transitions/presets/fade.ts +89 -0
  51. package/src/transitions/presets/flip.ts +263 -0
  52. package/src/transitions/presets/slide.ts +154 -0
  53. package/src/transitions/presets/wipe.ts +195 -0
  54. package/src/transitions/presets/zoom.ts +183 -0
  55. package/src/useAudioData.ts +260 -0
  56. package/src/useSpring.ts +215 -0
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Interface for spring preset configuration values.
3
+ */
4
+ export interface SpringPreset {
5
+ mass: number;
6
+ stiffness: number;
7
+ damping: number;
8
+ }
9
+
10
+ /**
11
+ * Options for the measureSpring function.
12
+ */
13
+ export interface MeasureSpringOptions {
14
+ /** Frames per second (required) */
15
+ fps: number;
16
+ /** Controls animation speed (lower = faster). Defaults to 1. */
17
+ mass?: number;
18
+ /** Bounciness (higher = more bounce). Defaults to 100. */
19
+ stiffness?: number;
20
+ /** Deceleration (higher = less bounce). Defaults to 10. */
21
+ damping?: number;
22
+ /** How close to final value to consider "settled". Defaults to 0.001. */
23
+ threshold?: number;
24
+ }
25
+
26
+ /**
27
+ * Measures how many frames a spring animation takes to settle.
28
+ *
29
+ * This is useful for:
30
+ * - Determining the duration of a Sequence containing spring animations
31
+ * - Setting Loop durationInFrames to match spring completion
32
+ * - Calculating total video duration with springs
33
+ *
34
+ * Usage:
35
+ * const frames = measureSpring({ fps: 30 });
36
+ * const frames = measureSpring({ fps: 30, mass: 0.5, stiffness: 200 });
37
+ *
38
+ * @param {MeasureSpringOptions} options
39
+ * @param {number} options.fps - Frames per second (required)
40
+ * @param {number} [options.mass=1] - Controls animation speed (lower = faster)
41
+ * @param {number} [options.stiffness=100] - Bounciness (higher = more bounce)
42
+ * @param {number} [options.damping=10] - Deceleration (higher = less bounce)
43
+ * @param {number} [options.threshold=0.001] - How close to final value to consider "settled"
44
+ * @returns {number} Number of frames until the spring settles
45
+ */
46
+ export function measureSpring(options: MeasureSpringOptions): number {
47
+ const {
48
+ fps,
49
+ mass = 1,
50
+ stiffness = 100,
51
+ damping = 10,
52
+ threshold = 0.001,
53
+ } = options;
54
+
55
+ if (!fps || fps <= 0) {
56
+ throw new Error('measureSpring requires a positive fps value');
57
+ }
58
+
59
+ // Calculate spring physics parameters
60
+ const omega0: number = Math.sqrt(stiffness / mass); // Natural frequency
61
+ const zeta: number = damping / (2 * Math.sqrt(stiffness * mass)); // Damping ratio
62
+
63
+ // Maximum time to search (10 seconds should be enough for any reasonable spring)
64
+ const maxTime: number = 10;
65
+ const maxFrames: number = Math.ceil(maxTime * fps);
66
+
67
+ // Simulate the spring frame by frame
68
+ for (let frame = 1; frame <= maxFrames; frame++) {
69
+ const t: number = frame / fps;
70
+ let displacement: number;
71
+
72
+ if (zeta < 1) {
73
+ // Underdamped (oscillates)
74
+ const omegaD: number = omega0 * Math.sqrt(1 - zeta * zeta);
75
+ displacement = Math.exp(-zeta * omega0 * t) *
76
+ (Math.cos(omegaD * t) + (zeta * omega0 / omegaD) * Math.sin(omegaD * t));
77
+ } else if (zeta === 1) {
78
+ // Critically damped
79
+ displacement = Math.exp(-omega0 * t) * (1 + omega0 * t);
80
+ } else {
81
+ // Overdamped
82
+ const s1: number = -omega0 * (zeta - Math.sqrt(zeta * zeta - 1));
83
+ const s2: number = -omega0 * (zeta + Math.sqrt(zeta * zeta - 1));
84
+ displacement = (s2 * Math.exp(s1 * t) - s1 * Math.exp(s2 * t)) / (s2 - s1);
85
+ }
86
+
87
+ // Check if we've settled (displacement from target is below threshold)
88
+ if (Math.abs(displacement) < threshold) {
89
+ return frame;
90
+ }
91
+ }
92
+
93
+ // If we didn't settle within maxFrames, return maxFrames
94
+ // This shouldn't happen with reasonable spring parameters
95
+ return maxFrames;
96
+ }
97
+
98
+ /**
99
+ * Calculate the natural frequency of a spring.
100
+ *
101
+ * @param {number} stiffness
102
+ * @param {number} mass
103
+ * @returns {number} Natural frequency in radians per second
104
+ */
105
+ export function springNaturalFrequency(stiffness: number, mass: number): number {
106
+ return Math.sqrt(stiffness / mass);
107
+ }
108
+
109
+ /**
110
+ * Calculate the damping ratio of a spring.
111
+ *
112
+ * - zeta < 1: Underdamped (oscillates)
113
+ * - zeta = 1: Critically damped (fastest without oscillation)
114
+ * - zeta > 1: Overdamped (slow, no oscillation)
115
+ *
116
+ * @param {number} damping
117
+ * @param {number} stiffness
118
+ * @param {number} mass
119
+ * @returns {number} Damping ratio
120
+ */
121
+ export function springDampingRatio(damping: number, stiffness: number, mass: number): number {
122
+ return damping / (2 * Math.sqrt(stiffness * mass));
123
+ }
124
+
125
+ /**
126
+ * Get spring configuration presets.
127
+ *
128
+ * Usage:
129
+ * const config = springPresets.gentle;
130
+ * spring(frame, { ...config, from: 0, to: 100 });
131
+ */
132
+ export const springPresets: Record<string, SpringPreset> = {
133
+ // Quick and snappy
134
+ snappy: { mass: 1, stiffness: 400, damping: 30 },
135
+
136
+ // Gentle, slow movement
137
+ gentle: { mass: 1, stiffness: 100, damping: 15 },
138
+
139
+ // Bouncy, playful
140
+ bouncy: { mass: 1, stiffness: 200, damping: 10 },
141
+
142
+ // Stiff, minimal overshoot
143
+ stiff: { mass: 1, stiffness: 300, damping: 25 },
144
+
145
+ // Slow, heavy feeling
146
+ slow: { mass: 2, stiffness: 100, damping: 20 },
147
+
148
+ // Default (matches Remotion)
149
+ default: { mass: 1, stiffness: 100, damping: 10 },
150
+ };
151
+
152
+ export default measureSpring;
package/src/noise.ts ADDED
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Noise Utilities
3
+ *
4
+ * Deterministic noise and random functions for reproducible animations.
5
+ * All functions are pure — same inputs always produce same outputs.
6
+ */
7
+
8
+ /**
9
+ * Options for 2D and 3D Perlin noise functions.
10
+ */
11
+ interface NoiseOptions {
12
+ /** Seed for the permutation table */
13
+ seed?: number;
14
+ /** Scales the input coordinates */
15
+ frequency?: number;
16
+ /** Scales the output value */
17
+ amplitude?: number;
18
+ }
19
+
20
+ /**
21
+ * Options for Fractal Brownian Motion (fBm).
22
+ */
23
+ interface FbmOptions extends NoiseOptions {
24
+ /** Number of noise layers */
25
+ octaves?: number;
26
+ /** Frequency multiplier per octave */
27
+ lacunarity?: number;
28
+ /** Amplitude multiplier per octave */
29
+ persistence?: number;
30
+ }
31
+
32
+ /** A 2D gradient vector [x, y] */
33
+ type Gradient2D = [number, number];
34
+
35
+ /** A 3D gradient vector [x, y, z] */
36
+ type Gradient3D = [number, number, number];
37
+
38
+ /**
39
+ * Generate a seeded pseudo-random number between 0 and 1.
40
+ *
41
+ * Uses a hash-based approach for single-call determinism (no state).
42
+ *
43
+ * @param {number} seed - Any number (typically frame number or index)
44
+ * @returns {number} Pseudo-random value in [0, 1)
45
+ */
46
+ export function random(seed: number): number {
47
+ // Mulberry32 — fast, good distribution, 32-bit state
48
+ let t: number = (seed + 0x6d2b79f5) | 0;
49
+ t = Math.imul(t ^ (t >>> 15), t | 1);
50
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
51
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
52
+ }
53
+
54
+ /**
55
+ * Generate a seeded random number within a range.
56
+ *
57
+ * @param {number} seed - Seed value
58
+ * @param {number} min - Minimum value (inclusive)
59
+ * @param {number} max - Maximum value (exclusive)
60
+ * @returns {number} Random value in [min, max)
61
+ */
62
+ export function randomRange(seed: number, min: number, max: number): number {
63
+ return min + random(seed) * (max - min);
64
+ }
65
+
66
+ /**
67
+ * Generate a seeded random integer within a range.
68
+ *
69
+ * @param {number} seed - Seed value
70
+ * @param {number} min - Minimum value (inclusive)
71
+ * @param {number} max - Maximum value (inclusive)
72
+ * @returns {number} Random integer in [min, max]
73
+ */
74
+ export function randomInt(seed: number, min: number, max: number): number {
75
+ return Math.floor(min + random(seed) * (max - min + 1));
76
+ }
77
+
78
+ // ─── Permutation table for Perlin noise ──────────────────────────────────────
79
+
80
+ function buildPermutation(seed: number): Uint8Array {
81
+ const perm: Uint8Array = new Uint8Array(512);
82
+ const base: Uint8Array = new Uint8Array(256);
83
+
84
+ for (let i = 0; i < 256; i++) {
85
+ base[i] = i;
86
+ }
87
+
88
+ // Fisher-Yates shuffle with seeded random
89
+ for (let i = 255; i > 0; i--) {
90
+ const j: number = Math.floor(random(seed + i) * (i + 1));
91
+ const tmp: number = base[i];
92
+ base[i] = base[j];
93
+ base[j] = tmp;
94
+ }
95
+
96
+ // Double the table to avoid index wrapping
97
+ for (let i = 0; i < 256; i++) {
98
+ perm[i] = base[i];
99
+ perm[i + 256] = base[i];
100
+ }
101
+
102
+ return perm;
103
+ }
104
+
105
+ // ─── Gradient vectors for 2D and 3D Perlin noise ────────────────────────────
106
+
107
+ const GRAD2: Gradient2D[] = [
108
+ [1, 0], [-1, 0], [0, 1], [0, -1],
109
+ [1, 1], [-1, 1], [1, -1], [-1, -1],
110
+ ];
111
+
112
+ const GRAD3: Gradient3D[] = [
113
+ [1, 1, 0], [-1, 1, 0], [1, -1, 0], [-1, -1, 0],
114
+ [1, 0, 1], [-1, 0, 1], [1, 0, -1], [-1, 0, -1],
115
+ [0, 1, 1], [0, -1, 1], [0, 1, -1], [0, -1, -1],
116
+ ];
117
+
118
+ function fade(t: number): number {
119
+ // Improved Perlin fade: 6t^5 - 15t^4 + 10t^3
120
+ return t * t * t * (t * (t * 6 - 15) + 10);
121
+ }
122
+
123
+ function lerp(a: number, b: number, t: number): number {
124
+ return a + t * (b - a);
125
+ }
126
+
127
+ function dot2(g: Gradient2D, x: number, y: number): number {
128
+ return g[0] * x + g[1] * y;
129
+ }
130
+
131
+ function dot3(g: Gradient3D, x: number, y: number, z: number): number {
132
+ return g[0] * x + g[1] * y + g[2] * z;
133
+ }
134
+
135
+ // ─── Cache for permutation tables ────────────────────────────────────────────
136
+
137
+ const permCache: Map<number, Uint8Array> = new Map();
138
+
139
+ function getPermutation(seed: number): Uint8Array {
140
+ if (!permCache.has(seed)) {
141
+ // Limit cache size
142
+ if (permCache.size > 32) {
143
+ const firstKey: number | undefined = permCache.keys().next().value;
144
+ if (firstKey !== undefined) {
145
+ permCache.delete(firstKey);
146
+ }
147
+ }
148
+ permCache.set(seed, buildPermutation(seed));
149
+ }
150
+ return permCache.get(seed)!;
151
+ }
152
+
153
+ /**
154
+ * 2D Perlin noise.
155
+ *
156
+ * Returns a smooth, continuous noise value for the given coordinates.
157
+ * Same inputs always produce the same output.
158
+ *
159
+ * @param {number} x - X coordinate
160
+ * @param {number} y - Y coordinate
161
+ * @param {object} [options]
162
+ * @param {number} [options.seed=0] - Seed for the permutation table
163
+ * @param {number} [options.frequency=1] - Scales the input coordinates
164
+ * @param {number} [options.amplitude=1] - Scales the output value
165
+ * @returns {number} Noise value in [-amplitude, amplitude]
166
+ */
167
+ export function noise2D(x: number, y: number, options: NoiseOptions = {}): number {
168
+ const { seed = 0, frequency = 1, amplitude = 1 } = options;
169
+ const perm: Uint8Array = getPermutation(seed);
170
+
171
+ x *= frequency;
172
+ y *= frequency;
173
+
174
+ // Grid cell coordinates
175
+ const xi: number = Math.floor(x) & 255;
176
+ const yi: number = Math.floor(y) & 255;
177
+
178
+ // Relative position within cell
179
+ const xf: number = x - Math.floor(x);
180
+ const yf: number = y - Math.floor(y);
181
+
182
+ // Fade curves
183
+ const u: number = fade(xf);
184
+ const v: number = fade(yf);
185
+
186
+ // Hash the 4 corners
187
+ const aa: number = perm[perm[xi] + yi];
188
+ const ab: number = perm[perm[xi] + yi + 1];
189
+ const ba: number = perm[perm[xi + 1] + yi];
190
+ const bb: number = perm[perm[xi + 1] + yi + 1];
191
+
192
+ // Gradient dot products
193
+ const g00: number = dot2(GRAD2[aa % 8], xf, yf);
194
+ const g10: number = dot2(GRAD2[ba % 8], xf - 1, yf);
195
+ const g01: number = dot2(GRAD2[ab % 8], xf, yf - 1);
196
+ const g11: number = dot2(GRAD2[bb % 8], xf - 1, yf - 1);
197
+
198
+ // Bilinear interpolation
199
+ const x1: number = lerp(g00, g10, u);
200
+ const x2: number = lerp(g01, g11, u);
201
+ const result: number = lerp(x1, x2, v);
202
+
203
+ return result * amplitude;
204
+ }
205
+
206
+ /**
207
+ * 3D Perlin noise.
208
+ *
209
+ * Useful for animated noise: use (x, y, frame * speed) as coordinates.
210
+ *
211
+ * @param {number} x - X coordinate
212
+ * @param {number} y - Y coordinate
213
+ * @param {number} z - Z coordinate (often frame * speed)
214
+ * @param {object} [options]
215
+ * @param {number} [options.seed=0] - Seed for the permutation table
216
+ * @param {number} [options.frequency=1] - Scales the input coordinates
217
+ * @param {number} [options.amplitude=1] - Scales the output value
218
+ * @returns {number} Noise value in [-amplitude, amplitude]
219
+ */
220
+ export function noise3D(x: number, y: number, z: number, options: NoiseOptions = {}): number {
221
+ const { seed = 0, frequency = 1, amplitude = 1 } = options;
222
+ const perm: Uint8Array = getPermutation(seed);
223
+
224
+ x *= frequency;
225
+ y *= frequency;
226
+ z *= frequency;
227
+
228
+ const xi: number = Math.floor(x) & 255;
229
+ const yi: number = Math.floor(y) & 255;
230
+ const zi: number = Math.floor(z) & 255;
231
+
232
+ const xf: number = x - Math.floor(x);
233
+ const yf: number = y - Math.floor(y);
234
+ const zf: number = z - Math.floor(z);
235
+
236
+ const u: number = fade(xf);
237
+ const v: number = fade(yf);
238
+ const w: number = fade(zf);
239
+
240
+ // Hash the 8 corners
241
+ const a: number = perm[xi] + yi;
242
+ const aa: number = perm[a] + zi;
243
+ const ab: number = perm[a + 1] + zi;
244
+ const b: number = perm[xi + 1] + yi;
245
+ const ba: number = perm[b] + zi;
246
+ const bb: number = perm[b + 1] + zi;
247
+
248
+ const g000: number = dot3(GRAD3[perm[aa] % 12], xf, yf, zf);
249
+ const g100: number = dot3(GRAD3[perm[ba] % 12], xf - 1, yf, zf);
250
+ const g010: number = dot3(GRAD3[perm[ab] % 12], xf, yf - 1, zf);
251
+ const g110: number = dot3(GRAD3[perm[bb] % 12], xf - 1, yf - 1, zf);
252
+ const g001: number = dot3(GRAD3[perm[aa + 1] % 12], xf, yf, zf - 1);
253
+ const g101: number = dot3(GRAD3[perm[ba + 1] % 12], xf - 1, yf, zf - 1);
254
+ const g011: number = dot3(GRAD3[perm[ab + 1] % 12], xf, yf - 1, zf - 1);
255
+ const g111: number = dot3(GRAD3[perm[bb + 1] % 12], xf - 1, yf - 1, zf - 1);
256
+
257
+ // Trilinear interpolation
258
+ const x1: number = lerp(g000, g100, u);
259
+ const x2: number = lerp(g010, g110, u);
260
+ const y1: number = lerp(x1, x2, v);
261
+
262
+ const x3: number = lerp(g001, g101, u);
263
+ const x4: number = lerp(g011, g111, u);
264
+ const y2: number = lerp(x3, x4, v);
265
+
266
+ const result: number = lerp(y1, y2, w);
267
+
268
+ return result * amplitude;
269
+ }
270
+
271
+ /**
272
+ * Fractal Brownian Motion (fBm) — layered 2D noise for natural textures.
273
+ *
274
+ * @param {number} x - X coordinate
275
+ * @param {number} y - Y coordinate
276
+ * @param {object} [options]
277
+ * @param {number} [options.seed=0] - Seed
278
+ * @param {number} [options.octaves=4] - Number of noise layers
279
+ * @param {number} [options.frequency=1] - Base frequency
280
+ * @param {number} [options.amplitude=1] - Base amplitude
281
+ * @param {number} [options.lacunarity=2] - Frequency multiplier per octave
282
+ * @param {number} [options.persistence=0.5] - Amplitude multiplier per octave
283
+ * @returns {number} Noise value
284
+ */
285
+ export function fbm2D(x: number, y: number, options: FbmOptions = {}): number {
286
+ const {
287
+ seed = 0,
288
+ octaves = 4,
289
+ frequency = 1,
290
+ amplitude = 1,
291
+ lacunarity = 2,
292
+ persistence = 0.5,
293
+ } = options;
294
+
295
+ let value: number = 0;
296
+ let freq: number = frequency;
297
+ let amp: number = amplitude;
298
+
299
+ for (let i = 0; i < octaves; i++) {
300
+ value += noise2D(x, y, { seed: seed + i * 1000, frequency: freq, amplitude: amp });
301
+ freq *= lacunarity;
302
+ amp *= persistence;
303
+ }
304
+
305
+ return value;
306
+ }
307
+
308
+ export default { random, randomRange, randomInt, noise2D, noise3D, fbm2D };