anim-engine 0.3.1 → 0.4.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.
Files changed (50) hide show
  1. package/README.md +8 -8
  2. package/dist/animation/create-animation.d.ts +1 -23
  3. package/dist/animation/create-animation.js +35 -41
  4. package/dist/animation/create-single-tween.d.ts +39 -0
  5. package/dist/animation/index.d.ts +1 -0
  6. package/dist/animation/runner.d.ts +5 -8
  7. package/dist/animation/runner.js +8 -62
  8. package/dist/animation/update.d.ts +1 -1
  9. package/dist/domain/animation.d.ts +40 -0
  10. package/dist/domain/color.d.ts +2 -0
  11. package/dist/domain/easing.d.ts +18 -0
  12. package/dist/{easing → domain}/easing.js +4 -37
  13. package/dist/domain/index.d.ts +10 -0
  14. package/dist/domain/interpolation.d.ts +35 -0
  15. package/dist/domain/resolve-value.d.ts +2 -0
  16. package/dist/domain/resolve-value.js +4 -0
  17. package/dist/domain/ticker.d.ts +8 -0
  18. package/dist/domain/timeline.d.ts +21 -0
  19. package/dist/index.d.ts +10 -17
  20. package/dist/index.js +7 -6
  21. package/dist/lerp/create-lerp.d.ts +1 -8
  22. package/dist/lerp/create-lerp.js +15 -15
  23. package/dist/lerp/index.d.ts +1 -0
  24. package/dist/lerp-rgba/hex-to-rgba.d.ts +13 -0
  25. package/dist/lerp-rgba/hex-to-rgba.js +48 -0
  26. package/dist/lerp-rgba/index.d.ts +2 -0
  27. package/dist/lerp-rgba/lerp-rgba.d.ts +13 -0
  28. package/dist/{color/lerp-oklab.js → lerp-rgba/lerp-rgba.js} +3 -48
  29. package/dist/smooth-clamp/{smooth-clamp.js → create-smooth-clamp.js} +1 -1
  30. package/dist/smooth-clamp/index.d.ts +1 -0
  31. package/dist/smooth-damp/create-smooth-damp.d.ts +1 -9
  32. package/dist/smooth-damp/create-smooth-damp.js +1 -1
  33. package/dist/smooth-damp/index.d.ts +1 -0
  34. package/dist/spring/create-spring.d.ts +1 -10
  35. package/dist/spring/create-spring.js +1 -1
  36. package/dist/spring/index.d.ts +1 -0
  37. package/dist/ticker/get-ticker.d.ts +16 -4
  38. package/dist/ticker/get-ticker.js +72 -2
  39. package/dist/ticker/index.d.ts +1 -0
  40. package/dist/timeline/create-timeline.d.ts +1 -21
  41. package/dist/timeline/create-timeline.js +8 -11
  42. package/dist/timeline/index.d.ts +1 -0
  43. package/package.json +3 -2
  44. package/dist/color/lerp-oklab.d.ts +0 -26
  45. package/dist/easing/easing.d.ts +0 -16
  46. package/dist/shared/internal.d.ts +0 -4
  47. package/dist/shared/types.d.ts +0 -28
  48. package/dist/ticker/ticker.d.ts +0 -22
  49. package/dist/ticker/ticker.js +0 -74
  50. /package/dist/smooth-clamp/{smooth-clamp.d.ts → create-smooth-clamp.d.ts} +0 -0
@@ -0,0 +1,4 @@
1
+ //#region src/domain/resolve-value.ts
2
+ var resolveValue = (value) => typeof value === "function" ? value() : value;
3
+ //#endregion
4
+ export { resolveValue };
@@ -0,0 +1,8 @@
1
+ export type TickHandler = (deltaMs: number) => void;
2
+ export type Ticker = {
3
+ start: () => void;
4
+ stop: () => void;
5
+ update: (deltaMs: number) => void;
6
+ add: (handler: TickHandler) => void;
7
+ remove: (handler: TickHandler) => void;
8
+ };
@@ -0,0 +1,21 @@
1
+ import { DynamicValue } from '../domain';
2
+ import { KeyframeAnimationOptions, AnimationStatus } from './animation';
3
+ export type TimelineLayer = {
4
+ animation: KeyframeAnimationOptions;
5
+ at: DynamicValue;
6
+ } | {
7
+ animation: KeyframeAnimationOptions;
8
+ gap: number;
9
+ };
10
+ export type Timeline = {
11
+ play: () => Promise<void>;
12
+ pause: () => void;
13
+ resume: () => void;
14
+ stop: () => void;
15
+ skipToEnd: () => void;
16
+ kill: () => void;
17
+ setProgress: (value: number) => void;
18
+ progress: number;
19
+ status: AnimationStatus;
20
+ durationMs: number;
21
+ };
package/dist/index.d.ts CHANGED
@@ -1,17 +1,10 @@
1
- export { createAnimation } from './animation/create-animation';
2
- export { createTimeline } from './timeline/create-timeline';
3
- export { createSpring } from './spring/create-spring';
4
- export { createSmoothDamp } from './smooth-damp/create-smooth-damp';
5
- export { createLerp } from './lerp/create-lerp';
6
- export { createSmoothClamp } from './smooth-clamp/smooth-clamp';
7
- export { lerpOklab, hexToRgba } from './color/lerp-oklab';
8
- export { getTicker } from './ticker/get-ticker';
9
- export { EASE_NAMES, cubicBezier } from './easing/easing';
10
- export type { Keyframe, AnimationOptions, KeyframeAnimationOptions, } from './animation/create-animation';
11
- export type { Animation, Interpolation, EaseName, AnimationStatus, InterpolationStatus, DynamicValue, } from './shared/types';
12
- export type { SpringOptions } from './spring/create-spring';
13
- export type { SmoothDampOptions } from './smooth-damp/create-smooth-damp';
14
- export type { LerpOptions } from './lerp/create-lerp';
15
- export type { TimelineLayer, Timeline } from './timeline/create-timeline';
16
- export type { RgbaTuple } from './color/lerp-oklab';
17
- export type { TickerControls } from './ticker/ticker';
1
+ export { createAnimation } from './animation';
2
+ export { createTimeline } from './timeline';
3
+ export { createLerp } from './lerp';
4
+ export { createSmoothDamp } from './smooth-damp';
5
+ export { createSmoothClamp } from './smooth-clamp';
6
+ export { createSpring } from './spring';
7
+ export { lerpRgba, hexToRgba } from './lerp-rgba';
8
+ export { getTicker } from './ticker';
9
+ export { cubicBezier } from './domain';
10
+ export type { EaseName, Interpolation, AnimationStatus, InterpolationStatus, DynamicValue, Animation, Keyframe, AnimationOptions, KeyframeAnimationOptions, TimelineLayer, Timeline, Ticker, LerpOptions, SmoothDampOptions, SpringOptions, RgbaTuple, } from './domain';
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
- import { EASE_NAMES, cubicBezier } from "./easing/easing.js";
1
+ import { cubicBezier } from "./domain/easing.js";
2
2
  import { getTicker } from "./ticker/get-ticker.js";
3
3
  import { createAnimation } from "./animation/create-animation.js";
4
4
  import { createTimeline } from "./timeline/create-timeline.js";
5
- import { createSpring } from "./spring/create-spring.js";
6
- import { createSmoothDamp } from "./smooth-damp/create-smooth-damp.js";
7
5
  import { createLerp } from "./lerp/create-lerp.js";
8
- import { createSmoothClamp } from "./smooth-clamp/smooth-clamp.js";
9
- import { hexToRgba, lerpOklab } from "./color/lerp-oklab.js";
10
- export { EASE_NAMES, createAnimation, createLerp, createSmoothClamp, createSmoothDamp, createSpring, createTimeline, cubicBezier, getTicker, hexToRgba, lerpOklab };
6
+ import { createSmoothDamp } from "./smooth-damp/create-smooth-damp.js";
7
+ import { createSmoothClamp } from "./smooth-clamp/create-smooth-clamp.js";
8
+ import { createSpring } from "./spring/create-spring.js";
9
+ import { lerpRgba } from "./lerp-rgba/lerp-rgba.js";
10
+ import { hexToRgba } from "./lerp-rgba/hex-to-rgba.js";
11
+ export { createAnimation, createLerp, createSmoothClamp, createSmoothDamp, createSpring, createTimeline, cubicBezier, getTicker, hexToRgba, lerpRgba };
@@ -1,9 +1,2 @@
1
- import { Interpolation, DynamicValue } from '../shared/types';
2
- export type LerpOptions = {
3
- to: () => number;
4
- smoothTimeMs: DynamicValue;
5
- precision?: number;
6
- onUpdate?: (value: number, velocity: number) => void;
7
- onEnded?: () => void;
8
- };
1
+ import { Interpolation, LerpOptions } from '../domain';
9
2
  export declare const createLerp: (options: LerpOptions) => Interpolation;
@@ -1,3 +1,4 @@
1
+ import { resolveValue } from "../domain/resolve-value.js";
1
2
  import { getTicker } from "../ticker/get-ticker.js";
2
3
  import { lerpStep } from "./step.js";
3
4
  //#region src/lerp/create-lerp.ts
@@ -10,9 +11,22 @@ var createLerp = (options) => {
10
11
  let currentVelocity = 0;
11
12
  let active = true;
12
13
  const ticker = getTicker();
13
- const resolveValue = (v) => typeof v === "function" ? v() : v;
14
14
  state.current = options.to();
15
15
  previousValue = state.current;
16
+ const update = (deltaMs) => {
17
+ if (!active) return;
18
+ const target = options.to();
19
+ const smoothTimeMs = resolveValue(options.smoothTimeMs);
20
+ lerpStep(state, target, smoothTimeMs, deltaMs);
21
+ currentVelocity = (state.current - previousValue) / (deltaMs / 1e3);
22
+ previousValue = state.current;
23
+ onUpdate?.(state.current, currentVelocity);
24
+ if (onEnded && Math.abs(state.current - target) < precision && Math.abs(currentVelocity) < precision) {
25
+ state.current = target;
26
+ currentVelocity = 0;
27
+ onEnded();
28
+ }
29
+ };
16
30
  ticker.add(update);
17
31
  const start = () => {
18
32
  if (active) return;
@@ -27,20 +41,6 @@ var createLerp = (options) => {
27
41
  active = false;
28
42
  ticker.remove(update);
29
43
  };
30
- function update(deltaMs) {
31
- if (!active) return;
32
- const target = options.to();
33
- const smoothTimeMs = resolveValue(options.smoothTimeMs);
34
- lerpStep(state, target, smoothTimeMs, deltaMs);
35
- currentVelocity = (state.current - previousValue) / (deltaMs / 1e3);
36
- previousValue = state.current;
37
- onUpdate?.(state.current, currentVelocity);
38
- if (onEnded && Math.abs(state.current - target) < precision && Math.abs(currentVelocity) < precision) {
39
- state.current = target;
40
- currentVelocity = 0;
41
- onEnded();
42
- }
43
- }
44
44
  return {
45
45
  start,
46
46
  stop,
@@ -0,0 +1 @@
1
+ export { createLerp } from './create-lerp';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Parse a hex color string into an RGBA tuple.
3
+ *
4
+ * Accepts formats:
5
+ * - `#RGB`
6
+ * - `#RGBA`
7
+ * - `#RRGGBB`
8
+ * - `#RRGGBBAA`
9
+ *
10
+ * @param hex - Hex color string with optional leading `#`.
11
+ * @returns An RGBA tuple [R, G, B, A], each 0–1.
12
+ */
13
+ export declare const hexToRgba: (hex: string) => [number, number, number, number];
@@ -0,0 +1,48 @@
1
+ //#region src/lerp-rgba/hex-to-rgba.ts
2
+ /**
3
+ * Parse a hex color string into an RGBA tuple.
4
+ *
5
+ * Accepts formats:
6
+ * - `#RGB`
7
+ * - `#RGBA`
8
+ * - `#RRGGBB`
9
+ * - `#RRGGBBAA`
10
+ *
11
+ * @param hex - Hex color string with optional leading `#`.
12
+ * @returns An RGBA tuple [R, G, B, A], each 0–1.
13
+ */
14
+ var hexToRgba = (hex) => {
15
+ const cleaned = hex.startsWith("#") ? hex.slice(1) : hex;
16
+ if (!/^[0-9a-fA-F]+$/.test(cleaned)) return [
17
+ 0,
18
+ 0,
19
+ 0,
20
+ 1
21
+ ];
22
+ if (cleaned.length === 3) return [
23
+ parseInt(cleaned[0] + cleaned[0], 16) / 255,
24
+ parseInt(cleaned[1] + cleaned[1], 16) / 255,
25
+ parseInt(cleaned[2] + cleaned[2], 16) / 255,
26
+ 1
27
+ ];
28
+ if (cleaned.length === 4) return [
29
+ parseInt(cleaned[0] + cleaned[0], 16) / 255,
30
+ parseInt(cleaned[1] + cleaned[1], 16) / 255,
31
+ parseInt(cleaned[2] + cleaned[2], 16) / 255,
32
+ parseInt(cleaned[3] + cleaned[3], 16) / 255
33
+ ];
34
+ if (cleaned.length >= 6) return [
35
+ parseInt(cleaned.slice(0, 2), 16) / 255,
36
+ parseInt(cleaned.slice(2, 4), 16) / 255,
37
+ parseInt(cleaned.slice(4, 6), 16) / 255,
38
+ cleaned.length >= 8 ? parseInt(cleaned.slice(6, 8), 16) / 255 : 1
39
+ ];
40
+ return [
41
+ 0,
42
+ 0,
43
+ 0,
44
+ 1
45
+ ];
46
+ };
47
+ //#endregion
48
+ export { hexToRgba };
@@ -0,0 +1,2 @@
1
+ export { lerpRgba } from './lerp-rgba';
2
+ export { hexToRgba } from './hex-to-rgba';
@@ -0,0 +1,13 @@
1
+ import { LerpRgba } from '../domain';
2
+ /**
3
+ * Interpolate between two RGBA colors in Oklab space.
4
+ *
5
+ * The RGB channels are converted to Oklab, lerped perceptually uniformly,
6
+ * and converted back. The alpha channel is lerped linearly.
7
+ *
8
+ * @param from - Starting RGBA tuple [R, G, B, A], each 0–1.
9
+ * @param to - Ending RGBA tuple [R, G, B, A], each 0–1.
10
+ * @param progress - Interpolation factor (0 = from, 1 = to).
11
+ * @returns A new RGBA tuple [R, G, B, A], each 0–1.
12
+ */
13
+ export declare const lerpRgba: LerpRgba;
@@ -1,4 +1,4 @@
1
- //#region src/color/lerp-oklab.ts
1
+ //#region src/lerp-rgba/lerp-rgba.ts
2
2
  /**
3
3
  * Pre-computed constants for Oklab conversion.
4
4
  *
@@ -121,7 +121,7 @@ var clamp = (value) => {
121
121
  * @param progress - Interpolation factor (0 = from, 1 = to).
122
122
  * @returns A new RGBA tuple [R, G, B, A], each 0–1.
123
123
  */
124
- var lerpOklab = (from, to, progress) => {
124
+ var lerpRgba = (from, to, progress) => {
125
125
  const clampedProgress = clamp(progress);
126
126
  const [lFrom, aFrom, bFrom] = rgbToOklab(from[0], from[1], from[2]);
127
127
  const [lTo, aTo, bTo] = rgbToOklab(to[0], to[1], to[2]);
@@ -133,50 +133,5 @@ var lerpOklab = (from, to, progress) => {
133
133
  clamp(from[3] + (to[3] - from[3]) * clampedProgress)
134
134
  ];
135
135
  };
136
- /**
137
- * Parse a hex color string into an RGBA tuple.
138
- *
139
- * Accepts formats:
140
- * - `#RGB`
141
- * - `#RGBA`
142
- * - `#RRGGBB`
143
- * - `#RRGGBBAA`
144
- *
145
- * @param hex - Hex color string with optional leading `#`.
146
- * @returns An RGBA tuple [R, G, B, A], each 0–1.
147
- */
148
- var hexToRgba = (hex) => {
149
- const cleaned = hex.startsWith("#") ? hex.slice(1) : hex;
150
- if (!/^[0-9a-fA-F]+$/.test(cleaned)) return [
151
- 0,
152
- 0,
153
- 0,
154
- 1
155
- ];
156
- if (cleaned.length === 3) return [
157
- parseInt(cleaned[0] + cleaned[0], 16) / 255,
158
- parseInt(cleaned[1] + cleaned[1], 16) / 255,
159
- parseInt(cleaned[2] + cleaned[2], 16) / 255,
160
- 1
161
- ];
162
- if (cleaned.length === 4) return [
163
- parseInt(cleaned[0] + cleaned[0], 16) / 255,
164
- parseInt(cleaned[1] + cleaned[1], 16) / 255,
165
- parseInt(cleaned[2] + cleaned[2], 16) / 255,
166
- parseInt(cleaned[3] + cleaned[3], 16) / 255
167
- ];
168
- if (cleaned.length >= 6) return [
169
- parseInt(cleaned.slice(0, 2), 16) / 255,
170
- parseInt(cleaned.slice(2, 4), 16) / 255,
171
- parseInt(cleaned.slice(4, 6), 16) / 255,
172
- cleaned.length >= 8 ? parseInt(cleaned.slice(6, 8), 16) / 255 : 1
173
- ];
174
- return [
175
- 0,
176
- 0,
177
- 0,
178
- 1
179
- ];
180
- };
181
136
  //#endregion
182
- export { hexToRgba, lerpOklab };
137
+ export { lerpRgba };
@@ -1,4 +1,4 @@
1
- //#region src/smooth-clamp/smooth-clamp.ts
1
+ //#region src/smooth-clamp/create-smooth-clamp.ts
2
2
  /**
3
3
  * Creates a smooth clamp function that maps input toward a threshold
4
4
  * without ever exceeding it. Uses `x / (1 + |x|)` for a natural-feeling
@@ -0,0 +1 @@
1
+ export { createSmoothClamp } from './create-smooth-clamp';
@@ -1,10 +1,2 @@
1
- import { Interpolation, DynamicValue } from '../shared/types';
2
- export type SmoothDampOptions = {
3
- to: () => number;
4
- smoothTimeMs: DynamicValue;
5
- maxSpeed?: DynamicValue;
6
- precision?: number;
7
- onUpdate?: (value: number, velocity: number) => void;
8
- onEnded?: () => void;
9
- };
1
+ import { Interpolation, SmoothDampOptions } from '../domain';
10
2
  export declare const createSmoothDamp: (options: SmoothDampOptions) => Interpolation;
@@ -1,3 +1,4 @@
1
+ import { resolveValue } from "../domain/resolve-value.js";
1
2
  import { getTicker } from "../ticker/get-ticker.js";
2
3
  import { smoothDampStep } from "./step.js";
3
4
  //#region src/smooth-damp/create-smooth-damp.ts
@@ -11,7 +12,6 @@ var createSmoothDamp = (options) => {
11
12
  };
12
13
  let active = true;
13
14
  const ticker = getTicker();
14
- const resolveValue = (v) => typeof v === "function" ? v() : v;
15
15
  state.current = options.to();
16
16
  ticker.add(update);
17
17
  const start = () => {
@@ -0,0 +1 @@
1
+ export { createSmoothDamp } from './create-smooth-damp';
@@ -1,11 +1,2 @@
1
- import { Interpolation, DynamicValue } from '../shared/types';
2
- export type SpringOptions = {
3
- to: () => number;
4
- stiffness?: DynamicValue;
5
- damping?: DynamicValue;
6
- mass?: DynamicValue;
7
- precision?: number;
8
- onUpdate?: (value: number, velocity: number) => void;
9
- onEnded?: () => void;
10
- };
1
+ import { Interpolation, SpringOptions } from '../domain';
11
2
  export declare const createSpring: (options: SpringOptions) => Interpolation;
@@ -1,3 +1,4 @@
1
+ import { resolveValue } from "../domain/resolve-value.js";
1
2
  import { getTicker } from "../ticker/get-ticker.js";
2
3
  import { verletStep } from "./verlet.js";
3
4
  //#region src/spring/create-spring.ts
@@ -5,7 +6,6 @@ var createSpring = (options) => {
5
6
  const precision = options.precision ?? .01;
6
7
  const onUpdate = options.onUpdate;
7
8
  const onEnded = options.onEnded;
8
- const resolveValue = (v) => typeof v === "function" ? v() : v;
9
9
  const rawTo = options.to;
10
10
  const rawStiffness = options.stiffness ?? 180;
11
11
  const rawDamping = options.damping ?? 12;
@@ -0,0 +1 @@
1
+ export { createSpring } from './create-spring';
@@ -1,5 +1,17 @@
1
- import { TickerControls } from './ticker';
1
+ import { Ticker } from '../domain';
2
2
  /** Returns the default ticker singleton. Created lazily on first access. */
3
- export declare const getTicker: () => TickerControls;
4
- /** Creates a new independent ticker instance (for testing or multi-loop setups). */
5
- export { createTicker } from './ticker';
3
+ export declare const getTicker: () => Ticker;
4
+ /**
5
+ * Create a ticker that drives active animations.
6
+ *
7
+ * Does NOT auto-start. The user must explicitly call either:
8
+ * - `start()` — begins a `requestAnimationFrame` loop
9
+ * - `update(deltaMs)` — drive manually from a game loop
10
+ *
11
+ * `add()` and `remove()` register/unregister animations without
12
+ * side effects on the rAF loop.
13
+ *
14
+ * Uses a flat array with undefined-tombstone removal for safe concurrent
15
+ * modification during iteration. Compacted after each frame.
16
+ */
17
+ export declare const createTicker: () => Ticker;
@@ -1,4 +1,3 @@
1
- import { createTicker } from "./ticker.js";
2
1
  //#region src/ticker/get-ticker.ts
3
2
  var singleton;
4
3
  /** Returns the default ticker singleton. Created lazily on first access. */
@@ -6,5 +5,76 @@ var getTicker = () => {
6
5
  if (!singleton) singleton = createTicker();
7
6
  return singleton;
8
7
  };
8
+ /**
9
+ * Create a ticker that drives active animations.
10
+ *
11
+ * Does NOT auto-start. The user must explicitly call either:
12
+ * - `start()` — begins a `requestAnimationFrame` loop
13
+ * - `update(deltaMs)` — drive manually from a game loop
14
+ *
15
+ * `add()` and `remove()` register/unregister animations without
16
+ * side effects on the rAF loop.
17
+ *
18
+ * Uses a flat array with undefined-tombstone removal for safe concurrent
19
+ * modification during iteration. Compacted after each frame.
20
+ */
21
+ var createTicker = () => {
22
+ const activeAnimations = [];
23
+ let needsCompact = false;
24
+ let animationFrameRequestId = void 0;
25
+ let previousFrameTime = void 0;
26
+ const start = () => {
27
+ if (animationFrameRequestId !== void 0) return;
28
+ previousFrameTime = void 0;
29
+ const tick = (now) => {
30
+ if (previousFrameTime !== void 0) {
31
+ const delta = now - previousFrameTime;
32
+ update(delta);
33
+ }
34
+ previousFrameTime = now;
35
+ animationFrameRequestId = requestAnimationFrame(tick);
36
+ };
37
+ animationFrameRequestId = requestAnimationFrame(tick);
38
+ };
39
+ const stop = () => {
40
+ if (animationFrameRequestId !== void 0) {
41
+ cancelAnimationFrame(animationFrameRequestId);
42
+ animationFrameRequestId = void 0;
43
+ }
44
+ previousFrameTime = void 0;
45
+ };
46
+ const update = (deltaMs) => {
47
+ for (let i = 0; i < activeAnimations.length; i++) {
48
+ const anim = activeAnimations[i];
49
+ if (anim) anim(deltaMs);
50
+ }
51
+ if (needsCompact) {
52
+ let writeIndex = 0;
53
+ for (let readIndex = 0; readIndex < activeAnimations.length; readIndex++) {
54
+ const anim = activeAnimations[readIndex];
55
+ if (anim !== void 0) activeAnimations[writeIndex++] = anim;
56
+ }
57
+ activeAnimations.length = writeIndex;
58
+ needsCompact = false;
59
+ }
60
+ };
61
+ const add = (handler) => {
62
+ activeAnimations.push(handler);
63
+ };
64
+ const remove = (handler) => {
65
+ const index = activeAnimations.indexOf(handler);
66
+ if (index >= 0) {
67
+ activeAnimations[index] = void 0;
68
+ needsCompact = true;
69
+ }
70
+ };
71
+ return {
72
+ start,
73
+ stop,
74
+ update,
75
+ add,
76
+ remove
77
+ };
78
+ };
9
79
  //#endregion
10
- export { getTicker };
80
+ export { createTicker, getTicker };
@@ -0,0 +1 @@
1
+ export { getTicker } from './get-ticker';
@@ -1,24 +1,4 @@
1
- import { KeyframeAnimationOptions } from '../animation/create-animation';
2
- import { AnimationStatus, DynamicValue } from '../shared/types';
3
- export type TimelineLayer = {
4
- animation: KeyframeAnimationOptions;
5
- at: DynamicValue;
6
- } | {
7
- animation: KeyframeAnimationOptions;
8
- gap: number;
9
- };
10
- export type Timeline = {
11
- play: () => Promise<Timeline>;
12
- pause: () => void;
13
- resume: () => void;
14
- stop: () => void;
15
- skipToEnd: () => void;
16
- kill: () => void;
17
- setProgress: (value: number) => void;
18
- progress: number;
19
- status: AnimationStatus;
20
- durationMs: number;
21
- };
1
+ import { TimelineLayer, Timeline } from '../domain';
22
2
  export declare const createTimeline: (layers: TimelineLayer[], options?: {
23
3
  onStarted?: () => void;
24
4
  onProgress?: (progress: number) => void;
@@ -1,10 +1,9 @@
1
- import { easingFunctions } from "../easing/easing.js";
1
+ import { resolveEasing } from "../domain/easing.js";
2
+ import { resolveValue } from "../domain/resolve-value.js";
2
3
  import { getTicker } from "../ticker/get-ticker.js";
3
4
  import { createKeyframeRunner } from "../animation/runner.js";
4
5
  //#region src/timeline/create-timeline.ts
5
- var noop = () => {};
6
- var resolveValue = (v) => typeof v === "function" ? v() : v;
7
- var resolveEasing = (ease) => typeof ease === "function" ? ease : easingFunctions[ease];
6
+ var noOp = () => {};
8
7
  var buildFromConfigs = (rawLayers) => {
9
8
  const activeLayers = [];
10
9
  let previousEndAt = 0;
@@ -40,8 +39,7 @@ var buildFromConfigs = (rawLayers) => {
40
39
  };
41
40
  };
42
41
  var createTimeline = (layers, options) => {
43
- const { onStarted, onEnded } = options ?? {};
44
- const onProgress = options?.onProgress ?? noop;
42
+ const { onStarted, onEnded, onProgress = noOp } = options ?? {};
45
43
  const rawLayers = layers;
46
44
  let state = buildFromConfigs(rawLayers);
47
45
  let activeLayers = state.activeLayers;
@@ -55,7 +53,7 @@ var createTimeline = (layers, options) => {
55
53
  timelineStatus = "stopped";
56
54
  ticker.remove(update);
57
55
  onEnded?.();
58
- resolvePromise?.(timeline);
56
+ resolvePromise?.();
59
57
  resolvePromise = void 0;
60
58
  };
61
59
  const update = (deltaMs) => {
@@ -105,7 +103,7 @@ var createTimeline = (layers, options) => {
105
103
  if (timelineStatus !== "playing" && timelineStatus !== "paused") return;
106
104
  timelineStatus = "stopped";
107
105
  ticker.remove(update);
108
- resolvePromise?.(timeline);
106
+ resolvePromise?.();
109
107
  resolvePromise = void 0;
110
108
  };
111
109
  const skipToEnd = () => {
@@ -116,7 +114,7 @@ var createTimeline = (layers, options) => {
116
114
  timelineStatus = "stopped";
117
115
  ticker.remove(update);
118
116
  onEnded?.();
119
- resolvePromise?.(timeline);
117
+ resolvePromise?.();
120
118
  resolvePromise = void 0;
121
119
  };
122
120
  const kill = () => {
@@ -140,7 +138,7 @@ var createTimeline = (layers, options) => {
140
138
  }
141
139
  remainingLayers = activeLayers.filter((l) => !l.ended).length;
142
140
  };
143
- const timeline = {
141
+ return {
144
142
  play,
145
143
  pause,
146
144
  resume,
@@ -158,7 +156,6 @@ var createTimeline = (layers, options) => {
158
156
  return totalDurationMs;
159
157
  }
160
158
  };
161
- return timeline;
162
159
  };
163
160
  //#endregion
164
161
  export { createTimeline };
@@ -0,0 +1 @@
1
+ export { createTimeline } from './create-timeline';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anim-engine",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "JavaScript library for animating numbers",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@
22
22
  "build": "npm run lint && npm run test && tsc && vite build",
23
23
  "preview": "vite preview",
24
24
  "check:types": "tsc --noEmit",
25
- "lint": "oxlint -c .oxlintrc.json src/",
25
+ "lint": "oxlint",
26
26
  "test": "vitest run",
27
27
  "test:watch": "vitest",
28
28
  "storybook": "storybook dev -p 6006",
@@ -42,6 +42,7 @@
42
42
  "gsap": "3.15.0",
43
43
  "oxfmt": "0.57.0",
44
44
  "oxlint": "1.72.0",
45
+ "oxlint-tsgolint": "^0.24.0",
45
46
  "storybook": "10.4.6",
46
47
  "typescript": "6.0.3",
47
48
  "vite": "8.1.3",
@@ -1,26 +0,0 @@
1
- export type RgbaTuple = readonly [number, number, number, number];
2
- /**
3
- * Interpolate between two RGBA colors in Oklab space.
4
- *
5
- * The RGB channels are converted to Oklab, lerped perceptually uniformly,
6
- * and converted back. The alpha channel is lerped linearly.
7
- *
8
- * @param from - Starting RGBA tuple [R, G, B, A], each 0–1.
9
- * @param to - Ending RGBA tuple [R, G, B, A], each 0–1.
10
- * @param progress - Interpolation factor (0 = from, 1 = to).
11
- * @returns A new RGBA tuple [R, G, B, A], each 0–1.
12
- */
13
- export declare const lerpOklab: (from: RgbaTuple, to: RgbaTuple, progress: number) => [number, number, number, number];
14
- /**
15
- * Parse a hex color string into an RGBA tuple.
16
- *
17
- * Accepts formats:
18
- * - `#RGB`
19
- * - `#RGBA`
20
- * - `#RRGGBB`
21
- * - `#RRGGBBAA`
22
- *
23
- * @param hex - Hex color string with optional leading `#`.
24
- * @returns An RGBA tuple [R, G, B, A], each 0–1.
25
- */
26
- export declare const hexToRgba: (hex: string) => [number, number, number, number];
@@ -1,16 +0,0 @@
1
- import { EaseName, EaseFunction } from '../shared/types';
2
- /** All 31 named easing identifiers, ordered by type. */
3
- export declare const EASE_NAMES: EaseName[];
4
- export declare const easingFunctions: Record<EaseName, EaseFunction>;
5
- /**
6
- * Cubic bezier easing. Builds a pre-computed lookup table at construction time
7
- * for O(log n) binary search at runtime — no Newton iteration, no allocation
8
- * per frame.
9
- *
10
- * @param p1x - First control point x coordinate.
11
- * @param p1y - First control point y coordinate.
12
- * @param p2x - Second control point x coordinate.
13
- * @param p2y - Second control point y coordinate.
14
- * @returns An EaseFunction suitable for use with animate() or createTimeline().
15
- */
16
- export declare const cubicBezier: (p1x: number, p1y: number, p2x: number, p2y: number) => EaseFunction;
@@ -1,4 +0,0 @@
1
- /** An object that can be driven by the ticker. */
2
- export type Updateable = {
3
- update(deltaMs: number): void;
4
- };