@ue-too/animate 0.9.5 → 0.11.0

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.
package/README.md CHANGED
@@ -1,5 +1,493 @@
1
- # Animate
1
+ # @ue-too/animate
2
2
 
3
- A simple animation library for HTML canvas applications.
3
+ Keyframe-based animation library for TypeScript canvas applications.
4
4
 
5
- Detailed information would be added in the future.
5
+ [![npm version](https://img.shields.io/npm/v/@ue-too/animate.svg)](https://www.npmjs.com/package/@ue-too/animate)
6
+ [![license](https://img.shields.io/npm/l/@ue-too/animate.svg)](https://github.com/ue-too/ue-too/blob/main/LICENSE.txt)
7
+
8
+ ## Overview
9
+
10
+ `@ue-too/animate` provides a flexible, composable animation system based on keyframes. It supports animating various types (numbers, points, colors, strings) with easing functions, delays, and complex animation sequencing through composition.
11
+
12
+ ### Key Features
13
+
14
+ - **Type-Safe Interpolation**: Built-in helpers for numbers, points (2D), RGB colors, strings, and integers
15
+ - **Keyframe System**: Define animations with values at specific progress points (0.0 to 1.0)
16
+ - **Composite Animations**: Sequence, overlap, and synchronize multiple animations
17
+ - **Easing Functions**: Custom timing curves for natural motion
18
+ - **Lifecycle Hooks**: `onStart`, `onEnd`, `setUp`, `tearDown` callbacks
19
+ - **Looping Support**: Finite and infinite loops with max loop counts
20
+ - **Delays and Drag**: Add delays before animation start and hold time after completion
21
+ - **Reverse Playback**: Play animations in reverse
22
+ - **Hierarchical Composition**: Nest composite animations for complex sequences
23
+
24
+ ## Installation
25
+
26
+ Using Bun:
27
+ ```bash
28
+ bun add @ue-too/animate
29
+ ```
30
+
31
+ Using npm:
32
+ ```bash
33
+ npm install @ue-too/animate
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ Here's a simple number animation example:
39
+
40
+ ```typescript
41
+ import { Animation, numberHelperFunctions } from '@ue-too/animate';
42
+
43
+ let opacity = 0;
44
+
45
+ // Create fade-in animation
46
+ const fadeIn = new Animation(
47
+ [
48
+ { percentage: 0, value: 0 }, // Start at 0% with value 0
49
+ { percentage: 1, value: 1 } // End at 100% with value 1
50
+ ],
51
+ (value) => { opacity = value; }, // Apply function updates the value
52
+ numberHelperFunctions, // Number interpolation helper
53
+ 1000 // Duration in milliseconds
54
+ );
55
+
56
+ // Start the animation
57
+ fadeIn.start();
58
+
59
+ // In your animation loop (e.g., requestAnimationFrame)
60
+ function gameLoop(deltaTime: number) {
61
+ fadeIn.animate(deltaTime); // Update animation with elapsed time
62
+ console.log('Opacity:', opacity);
63
+ requestAnimationFrame(() => gameLoop(16)); // ~60 FPS
64
+ }
65
+ ```
66
+
67
+ ## Core Concepts
68
+
69
+ ### Keyframes
70
+
71
+ Keyframes define values at specific points in an animation's progress:
72
+
73
+ ```typescript
74
+ type Keyframe<T> = {
75
+ percentage: number; // 0.0 (start) to 1.0 (end)
76
+ value: T; // Value at this point
77
+ easingFn?: (t: number) => number; // Optional easing for this segment
78
+ };
79
+ ```
80
+
81
+ **Example with easing:**
82
+ ```typescript
83
+ const keyframes = [
84
+ { percentage: 0, value: 0 },
85
+ { percentage: 0.5, value: 100, easingFn: (t) => t * t }, // Ease-in quadratic
86
+ { percentage: 1, value: 200 }
87
+ ];
88
+ ```
89
+
90
+ ### Animation Helpers
91
+
92
+ Helpers provide type-specific interpolation logic:
93
+
94
+ ```typescript
95
+ interface AnimatableAttributeHelper<T> {
96
+ lerp(ratio: number, start: Keyframe<T>, end: Keyframe<T>): T;
97
+ }
98
+ ```
99
+
100
+ ## Core APIs
101
+
102
+ ### Animation Class
103
+
104
+ Single-value keyframe animation.
105
+
106
+ ```typescript
107
+ const animation = new Animation<T>(
108
+ keyframes: Keyframe<T>[],
109
+ applyFn: (value: T) => void,
110
+ helper: AnimatableAttributeHelper<T>,
111
+ duration: number
112
+ );
113
+ ```
114
+
115
+ **Methods:**
116
+ - `start()`: Start the animation
117
+ - `stop()`: Stop and reset the animation
118
+ - `pause()`: Pause at current position
119
+ - `resume()`: Resume from paused state
120
+ - `animate(deltaTime: number)`: Update animation (call in your loop)
121
+ - `onStart(callback: Function)`: Subscribe to start event
122
+ - `onEnd(callback: Function)`: Subscribe to end event
123
+ - `setUp()`: Initialize animation state (called automatically)
124
+ - `tearDown()`: Clean up animation state
125
+
126
+ **Properties:**
127
+ - `loops: boolean`: Whether animation loops
128
+ - `maxLoopCount?: number`: Maximum number of loops (undefined = infinite)
129
+ - `duration: number`: Animation duration in milliseconds
130
+ - `delay: number`: Delay before animation starts
131
+ - `drag: number`: Hold time after animation completes
132
+ - `playing: boolean`: Whether animation is currently playing
133
+
134
+ ### CompositeAnimation Class
135
+
136
+ Container for sequencing multiple animations.
137
+
138
+ ```typescript
139
+ const composite = new CompositeAnimation(
140
+ animations?: Map<string, {animator: Animator, startTime?: number}>,
141
+ loop?: boolean,
142
+ parent?: AnimatorContainer,
143
+ setupFn?: Function,
144
+ tearDownFn?: Function
145
+ );
146
+ ```
147
+
148
+ **Methods:**
149
+ - `addAnimation(name: string, animator: Animator, startTime: number)`: Add animation at specific time
150
+ - `addAnimationAfter(name: string, animator: Animator, after: string)`: Add after another animation
151
+ - `addAnimationBefore(name: string, animator: Animator, before: string)`: Add before another animation
152
+ - `addAnimationAmidst(name: string, animator: Animator, during: string, offset: number)`: Overlap with another animation
153
+ - `start()`, `stop()`, `pause()`, `resume()`: Lifecycle control
154
+ - `animate(deltaTime: number)`: Update all child animations
155
+
156
+ ### Built-in Helpers
157
+
158
+ #### `numberHelperFunctions`
159
+
160
+ Linear interpolation for numbers:
161
+
162
+ ```typescript
163
+ import { Animation, numberHelperFunctions } from '@ue-too/animate';
164
+
165
+ let scale = 1;
166
+ const scaleAnimation = new Animation(
167
+ [
168
+ { percentage: 0, value: 1 },
169
+ { percentage: 1, value: 2 }
170
+ ],
171
+ (value) => { scale = value; },
172
+ numberHelperFunctions,
173
+ 500
174
+ );
175
+ ```
176
+
177
+ #### `pointHelperFunctions`
178
+
179
+ Interpolate 2D points (requires `@ue-too/math`):
180
+
181
+ ```typescript
182
+ import { Animation, pointHelperFunctions } from '@ue-too/animate';
183
+ import { Point } from '@ue-too/math';
184
+
185
+ let position: Point = { x: 0, y: 0 };
186
+
187
+ const moveAnimation = new Animation(
188
+ [
189
+ { percentage: 0, value: { x: 0, y: 0 } },
190
+ { percentage: 1, value: { x: 100, y: 100 } }
191
+ ],
192
+ (value) => { position = value; },
193
+ pointHelperFunctions,
194
+ 1000
195
+ );
196
+ ```
197
+
198
+ #### `rgbHelperFunctions`
199
+
200
+ Interpolate RGB colors:
201
+
202
+ ```typescript
203
+ import { Animation, rgbHelperFunctions, RGB } from '@ue-too/animate';
204
+
205
+ let color: RGB = { r: 255, g: 0, b: 0 };
206
+
207
+ const colorAnimation = new Animation(
208
+ [
209
+ { percentage: 0, value: { r: 255, g: 0, b: 0 } }, // Red
210
+ { percentage: 0.5, value: { r: 255, g: 255, b: 0 } }, // Yellow
211
+ { percentage: 1, value: { r: 0, g: 255, b: 0 } } // Green
212
+ ],
213
+ (value) => { color = value; },
214
+ rgbHelperFunctions,
215
+ 2000
216
+ );
217
+ ```
218
+
219
+ #### `stringHelperFunctions`
220
+
221
+ Step-based interpolation for strings (switches at 50%):
222
+
223
+ ```typescript
224
+ import { Animation, stringHelperFunctions } from '@ue-too/animate';
225
+
226
+ let state = 'idle';
227
+
228
+ const stateAnimation = new Animation(
229
+ [
230
+ { percentage: 0, value: 'idle' },
231
+ { percentage: 1, value: 'active' }
232
+ ],
233
+ (value) => { state = value; },
234
+ stringHelperFunctions,
235
+ 500
236
+ );
237
+ ```
238
+
239
+ #### `integerHelperFunctions`
240
+
241
+ Step-based interpolation for discrete integers:
242
+
243
+ ```typescript
244
+ import { Animation, integerHelperFunctions } from '@ue-too/animate';
245
+
246
+ let frameIndex = 0;
247
+
248
+ const frameAnimation = new Animation(
249
+ [
250
+ { percentage: 0, value: 0 },
251
+ { percentage: 0.33, value: 1 },
252
+ { percentage: 0.66, value: 2 },
253
+ { percentage: 1, value: 3 }
254
+ ],
255
+ (value) => { frameIndex = value; },
256
+ integerHelperFunctions,
257
+ 400
258
+ );
259
+ ```
260
+
261
+ ## Common Use Cases
262
+
263
+ ### Fade In/Out Effect
264
+
265
+ ```typescript
266
+ import { Animation, numberHelperFunctions } from '@ue-too/animate';
267
+
268
+ let opacity = 0;
269
+
270
+ const fadeIn = new Animation(
271
+ [
272
+ { percentage: 0, value: 0 },
273
+ { percentage: 1, value: 1, easingFn: (t) => t * t } // Ease-in
274
+ ],
275
+ (value) => { opacity = value; },
276
+ numberHelperFunctions,
277
+ 500
278
+ );
279
+
280
+ const fadeOut = new Animation(
281
+ [
282
+ { percentage: 0, value: 1 },
283
+ { percentage: 1, value: 0, easingFn: (t) => 1 - (1 - t) * (1 - t) } // Ease-out
284
+ ],
285
+ (value) => { opacity = value; },
286
+ numberHelperFunctions,
287
+ 500
288
+ );
289
+ ```
290
+
291
+ ### Animated Sprite Position
292
+
293
+ ```typescript
294
+ import { Animation, pointHelperFunctions } from '@ue-too/animate';
295
+ import { Point } from '@ue-too/math';
296
+
297
+ let spritePosition: Point = { x: 0, y: 0 };
298
+
299
+ const bounce = new Animation(
300
+ [
301
+ { percentage: 0, value: { x: 0, y: 0 } },
302
+ { percentage: 0.5, value: { x: 0, y: -50 }, easingFn: (t) => 1 - Math.pow(1 - t, 3) }, // Ease-out up
303
+ { percentage: 1, value: { x: 0, y: 0 }, easingFn: (t) => t * t * t } // Ease-in down
304
+ ],
305
+ (value) => { spritePosition = value; },
306
+ pointHelperFunctions,
307
+ 1000
308
+ );
309
+
310
+ bounce.loops = true; // Loop forever
311
+ ```
312
+
313
+ ### Sequential Animation Sequence
314
+
315
+ ```typescript
316
+ import { Animation, CompositeAnimation, numberHelperFunctions, pointHelperFunctions } from '@ue-too/animate';
317
+
318
+ let x = 0, y = 0, opacity = 0;
319
+
320
+ // Create individual animations
321
+ const fadeIn = new Animation(
322
+ [{ percentage: 0, value: 0 }, { percentage: 1, value: 1 }],
323
+ (value) => { opacity = value; },
324
+ numberHelperFunctions,
325
+ 500
326
+ );
327
+
328
+ const slideRight = new Animation(
329
+ [{ percentage: 0, value: 0 }, { percentage: 1, value: 100 }],
330
+ (value) => { x = value; },
331
+ numberHelperFunctions,
332
+ 500
333
+ );
334
+
335
+ const slideDown = new Animation(
336
+ [{ percentage: 0, value: 0 }, { percentage: 1, value: 50 }],
337
+ (value) => { y = value; },
338
+ numberHelperFunctions,
339
+ 300
340
+ );
341
+
342
+ // Create sequence: fade in, then slide right, then slide down
343
+ const sequence = new CompositeAnimation();
344
+ sequence.addAnimation('fadeIn', fadeIn, 0);
345
+ sequence.addAnimationAfter('slideRight', slideRight, 'fadeIn');
346
+ sequence.addAnimationAfter('slideDown', slideDown, 'slideRight');
347
+
348
+ sequence.start();
349
+
350
+ // Update in game loop
351
+ function update(deltaTime: number) {
352
+ sequence.animate(deltaTime);
353
+ // Render sprite at (x, y) with opacity
354
+ requestAnimationFrame(() => update(16));
355
+ }
356
+ ```
357
+
358
+ ### Overlapping Animations
359
+
360
+ ```typescript
361
+ const sequence = new CompositeAnimation();
362
+
363
+ // Start fade in at time 0
364
+ sequence.addAnimation('fadeIn', fadeInAnimation, 0);
365
+
366
+ // Start slide 200ms after fade in starts (overlap)
367
+ sequence.addAnimationAmidst('slide', slideAnimation, 'fadeIn', 200);
368
+
369
+ // Start scale after fade completes
370
+ sequence.addAnimationAfter('scale', scaleAnimation, 'fadeIn');
371
+ ```
372
+
373
+ ### Animation with Callbacks
374
+
375
+ ```typescript
376
+ const animation = new Animation(/* ... */);
377
+
378
+ animation.onStart(() => {
379
+ console.log('Animation started!');
380
+ });
381
+
382
+ animation.onEnd(() => {
383
+ console.log('Animation completed!');
384
+ // Trigger next action
385
+ });
386
+
387
+ animation.start();
388
+ ```
389
+
390
+ ### Looping with Max Count
391
+
392
+ ```typescript
393
+ const bounceAnimation = new Animation(/* ... */);
394
+ bounceAnimation.loops = true;
395
+ bounceAnimation.maxLoopCount = 3; // Bounce 3 times then stop
396
+
397
+ bounceAnimation.start();
398
+ ```
399
+
400
+ ### Custom Easing Functions
401
+
402
+ Common easing functions:
403
+
404
+ ```typescript
405
+ // Ease-in quadratic
406
+ const easeIn = (t: number) => t * t;
407
+
408
+ // Ease-out quadratic
409
+ const easeOut = (t: number) => 1 - (1 - t) * (1 - t);
410
+
411
+ // Ease-in-out quadratic
412
+ const easeInOut = (t: number) =>
413
+ t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
414
+
415
+ // Elastic ease-out
416
+ const elasticOut = (t: number) => {
417
+ const c4 = (2 * Math.PI) / 3;
418
+ return t === 0 ? 0 : t === 1 ? 1
419
+ : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
420
+ };
421
+
422
+ // Use in keyframe
423
+ const keyframe = {
424
+ percentage: 1,
425
+ value: 100,
426
+ easingFn: easeInOut
427
+ };
428
+ ```
429
+
430
+ ## API Reference
431
+
432
+ For complete API documentation with detailed type information, see the [TypeDoc-generated documentation](../../docs/animate).
433
+
434
+ ## TypeScript Support
435
+
436
+ This package is written in TypeScript with complete type definitions:
437
+
438
+ ```typescript
439
+ // Animations are fully typed
440
+ type Position = { x: number; y: number };
441
+
442
+ const posAnimation: Animation<Position> = new Animation(
443
+ [{ percentage: 0, value: { x: 0, y: 0 } }],
444
+ (value: Position) => { /* ... */ },
445
+ pointHelperFunctions,
446
+ 1000
447
+ );
448
+
449
+ // Custom helper functions are type-safe
450
+ const myHelper: AnimatableAttributeHelper<number> = {
451
+ lerp: (ratio, start, end) => {
452
+ // TypeScript knows start.value and end.value are numbers
453
+ return start.value + ratio * (end.value - start.value);
454
+ }
455
+ };
456
+ ```
457
+
458
+ ## Design Philosophy
459
+
460
+ This animation library follows these principles:
461
+
462
+ - **Composition over monoliths**: Build complex animations from simple pieces
463
+ - **Type safety**: Leverage TypeScript for compile-time correctness
464
+ - **Frame-independent**: Animations work with any frame rate (use deltaTime)
465
+ - **Declarative keyframes**: Define what you want, not how to get there
466
+ - **Flexible timing**: Delays, drag, loops, and easing for fine control
467
+
468
+ ## Performance Considerations
469
+
470
+ - **Update frequency**: Call `animate(deltaTime)` in your game loop at consistent intervals
471
+ - **Keyframe count**: More keyframes = more interpolation calculations (typically negligible)
472
+ - **Composite depth**: Deeply nested composites add minimal overhead
473
+ - **Memory**: Each animation retains keyframe data and callbacks
474
+
475
+ **Performance Tips:**
476
+ - Reuse animation instances when possible
477
+ - Use composite animations to group related animations
478
+ - Unsubscribe from callbacks (`onStart`, `onEnd`) when no longer needed
479
+ - For simple animations, consider direct property updates instead of keyframes
480
+
481
+ ## Related Packages
482
+
483
+ - **[@ue-too/math](../math)**: Vector operations for point animations
484
+ - **[@ue-too/curve](../curve)**: Bezier curves that can be animated
485
+ - **[@ue-too/board](../board)**: Canvas board that can use animations for transitions
486
+
487
+ ## License
488
+
489
+ MIT
490
+
491
+ ## Repository
492
+
493
+ [https://github.com/ue-too/ue-too](https://github.com/ue-too/ue-too)
@@ -1,37 +1,146 @@
1
1
  import { Point } from "@ue-too/math";
2
+ /**
3
+ * Represents a keyframe in an animation timeline.
4
+ *
5
+ * @remarks
6
+ * A keyframe defines a value at a specific point in the animation's progress.
7
+ * Keyframes are defined with a percentage from 0.0 (start) to 1.0 (end), along
8
+ * with the value at that point and an optional easing function for interpolation
9
+ * to the next keyframe.
10
+ *
11
+ * @typeParam T - The type of value being animated (number, Point, RGB, etc.)
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const keyframe: Keyframe<number> = {
16
+ * percentage: 0.5,
17
+ * value: 50,
18
+ * easingFn: (t) => t * t // Ease-in quadratic
19
+ * };
20
+ * ```
21
+ *
22
+ * @category Types
23
+ */
2
24
  export type Keyframe<T> = {
25
+ /** Animation progress from 0.0 (start) to 1.0 (end) */
3
26
  percentage: number;
27
+ /** Value at this keyframe */
4
28
  value: T;
29
+ /** Optional easing function for interpolation to next keyframe */
5
30
  easingFn?: (percentage: number) => number;
6
31
  };
32
+ /**
33
+ * Interface for type-specific interpolation helpers.
34
+ *
35
+ * @remarks
36
+ * Animation helpers provide the `lerp` (linear interpolation) logic for specific types.
37
+ * Different types require different interpolation strategies:
38
+ * - Numbers: Simple linear interpolation
39
+ * - Points: Component-wise interpolation
40
+ * - Colors (RGB): Component-wise color interpolation
41
+ * - Strings: Step-based (threshold) interpolation
42
+ *
43
+ * @typeParam T - The type of value being interpolated
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const myHelper: AnimatableAttributeHelper<number> = {
48
+ * lerp: (ratio, start, end) => {
49
+ * const t = (ratio - start.percentage) / (end.percentage - start.percentage);
50
+ * return start.value + t * (end.value - start.value);
51
+ * }
52
+ * };
53
+ * ```
54
+ *
55
+ * @category Helpers
56
+ */
7
57
  export interface AnimatableAttributeHelper<T> {
58
+ /**
59
+ * Interpolates between two keyframes at a given ratio.
60
+ *
61
+ * @param ratio - Current animation progress (0.0 to 1.0)
62
+ * @param start - Starting keyframe
63
+ * @param end - Ending keyframe
64
+ * @returns Interpolated value at the given ratio
65
+ */
8
66
  lerp(ratio: number, start: Keyframe<T>, end: Keyframe<T>): T;
9
67
  }
68
+ /**
69
+ * Built-in interpolation helper for animating Point values.
70
+ *
71
+ * @remarks
72
+ * Provides linear interpolation for 2D points with optional easing.
73
+ * Interpolates both x and y components independently.
74
+ *
75
+ * @category Helpers
76
+ */
10
77
  export declare const pointHelperFunctions: AnimatableAttributeHelper<Point>;
11
78
  export declare class PointAnimationHelper implements AnimatableAttributeHelper<Point> {
12
79
  constructor();
13
80
  lerp(ratio: number, start: Keyframe<Point>, end: Keyframe<Point>): Point;
14
81
  }
82
+ /**
83
+ * Built-in interpolation helper for animating number values.
84
+ *
85
+ * @remarks
86
+ * Provides linear interpolation for numeric values with optional easing.
87
+ *
88
+ * @category Helpers
89
+ */
15
90
  export declare const numberHelperFunctions: AnimatableAttributeHelper<number>;
16
91
  export declare class NumberAnimationHelper implements AnimatableAttributeHelper<number> {
17
92
  constructor();
18
93
  lerp(ratio: number, start: Keyframe<number>, end: Keyframe<number>): number;
19
94
  }
95
+ /**
96
+ * Built-in interpolation helper for animating string values.
97
+ *
98
+ * @remarks
99
+ * Uses step-based interpolation with a 50% threshold. Returns start value until
100
+ * 50% progress, then switches to end value. Useful for discrete property changes.
101
+ *
102
+ * @category Helpers
103
+ */
20
104
  export declare const stringHelperFunctions: AnimatableAttributeHelper<string>;
21
105
  export declare class StringAnimationHelper implements AnimatableAttributeHelper<string> {
22
106
  constructor();
23
107
  lerp(ratio: number, start: Keyframe<string>, end: Keyframe<string>): string;
24
108
  }
109
+ /**
110
+ * Built-in interpolation helper for animating integer values.
111
+ *
112
+ * @remarks
113
+ * Uses step-based interpolation with a 50% threshold, similar to strings.
114
+ * Useful for discrete numeric properties like indices or counts.
115
+ *
116
+ * @category Helpers
117
+ */
25
118
  export declare const integerHelperFunctions: AnimatableAttributeHelper<number>;
26
119
  export declare class IntegerAnimationHelper implements AnimatableAttributeHelper<number> {
27
120
  constructor();
28
121
  lerp(ratio: number, start: Keyframe<number>, end: Keyframe<number>): number;
29
122
  }
123
+ /**
124
+ * RGB color type for color animations.
125
+ *
126
+ * @remarks
127
+ * Represents a color with red, green, and blue components (0-255).
128
+ *
129
+ * @category Types
130
+ */
30
131
  export type RGB = {
31
132
  r: number;
32
133
  g: number;
33
134
  b: number;
34
135
  };
136
+ /**
137
+ * Built-in interpolation helper for animating RGB color values.
138
+ *
139
+ * @remarks
140
+ * Provides linear interpolation for RGB colors with component-wise blending.
141
+ *
142
+ * @category Helpers
143
+ */
35
144
  export declare const rgbHelperFunctions: AnimatableAttributeHelper<RGB>;
36
145
  export declare class RGBAnimationHelper implements AnimatableAttributeHelper<RGB> {
37
146
  constructor();