anim-engine 0.3.2 → 0.5.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 (46) hide show
  1. package/README.md +283 -499
  2. package/dist/animation/create-animation.d.ts +12 -0
  3. package/dist/animation/create-animation.js +19 -23
  4. package/dist/animation/create-single-tween.d.ts +1 -2
  5. package/dist/animation/index.d.ts +1 -0
  6. package/dist/animation/runner.d.ts +1 -1
  7. package/dist/animation/runner.js +22 -22
  8. package/dist/animation/update.d.ts +1 -1
  9. package/dist/animation/update.js +4 -4
  10. package/dist/domain/animation.d.ts +118 -3
  11. package/dist/domain/color.d.ts +14 -0
  12. package/dist/domain/easing.d.ts +12 -0
  13. package/dist/domain/easing.js +9 -35
  14. package/dist/domain/index.d.ts +4 -4
  15. package/dist/domain/interpolation.d.ts +153 -5
  16. package/dist/domain/resolve-value.d.ts +13 -0
  17. package/dist/domain/resolve-value.js +8 -0
  18. package/dist/domain/ticker.d.ts +26 -14
  19. package/dist/domain/timeline.d.ts +47 -2
  20. package/dist/index.d.ts +10 -13
  21. package/dist/index.js +8 -7
  22. package/dist/lerp/create-lerp.d.ts +14 -9
  23. package/dist/lerp/create-lerp.js +21 -18
  24. package/dist/lerp/index.d.ts +1 -0
  25. package/dist/lerp-rgba/hex-to-rgba.d.ts +13 -0
  26. package/dist/lerp-rgba/hex-to-rgba.js +48 -0
  27. package/dist/lerp-rgba/index.d.ts +2 -0
  28. package/dist/lerp-rgba/lerp-rgba.d.ts +13 -0
  29. package/dist/{color/lerp-oklab.js → lerp-rgba/lerp-rgba.js} +3 -48
  30. package/dist/smooth-clamp/{smooth-clamp.js → create-smooth-clamp.js} +1 -1
  31. package/dist/smooth-clamp/index.d.ts +1 -0
  32. package/dist/smooth-damp/create-smooth-damp.d.ts +16 -10
  33. package/dist/smooth-damp/create-smooth-damp.js +34 -29
  34. package/dist/smooth-damp/index.d.ts +1 -0
  35. package/dist/spring/create-spring.d.ts +13 -11
  36. package/dist/spring/create-spring.js +17 -19
  37. package/dist/spring/index.d.ts +1 -0
  38. package/dist/ticker/get-ticker.d.ts +25 -0
  39. package/dist/{domain/ticker.js → ticker/get-ticker.js} +10 -2
  40. package/dist/ticker/index.d.ts +1 -0
  41. package/dist/timeline/create-timeline.d.ts +16 -6
  42. package/dist/timeline/create-timeline.js +41 -13
  43. package/dist/timeline/index.d.ts +1 -0
  44. package/package.json +1 -1
  45. package/dist/color/lerp-oklab.d.ts +0 -26
  46. /package/dist/smooth-clamp/{smooth-clamp.d.ts → create-smooth-clamp.d.ts} +0 -0
@@ -1,10 +1,158 @@
1
- export type InterpolationStatus = "active" | "inactive" | "dead";
1
+ import { DynamicValue } from './resolve-value';
2
+ import { ExternalTicker } from './ticker';
3
+ /**
4
+ * Represents the status of an interpolation, which can be either "active" or "inactive".
5
+ */
6
+ export type InterpolationStatus = "active" | "inactive";
7
+ /**
8
+ * Represents an interpolation process that can be controlled and monitored.
9
+ *
10
+ * The interpolation can be resumed or stopped, and its current value, velocity, and status can be accessed.
11
+ */
2
12
  export type Interpolation = {
3
- start: () => void;
13
+ /**
14
+ * Resumes the interpolation if it is currently inactive.
15
+ */
16
+ resume: () => void;
17
+ /**
18
+ * Stops the interpolation if it is currently active.
19
+ */
4
20
  stop: () => void;
5
- kill: () => void;
6
- setCurrentValue: (value: number) => void;
7
- currentValue: number;
21
+ /**
22
+ * Sets the current value of the interpolation instantly and resets its velocity to zero.
23
+ */
24
+ setValue: (value: number) => void;
25
+ /**
26
+ * The current value of the interpolation.
27
+ */
28
+ value: number;
29
+ /**
30
+ * The current velocity of the interpolation.
31
+ */
8
32
  velocity: number;
33
+ /**
34
+ * The current status of the interpolation, which can be "active" or "inactive".
35
+ */
9
36
  status: InterpolationStatus;
10
37
  };
38
+ /**
39
+ * Options for configuring a linear interpolation (lerp) function.
40
+ */
41
+ export type LerpOptions = {
42
+ /**
43
+ * A function that returns the starting value for the interpolation. This is evaluated every frame.
44
+ */
45
+ to: () => number;
46
+ /**
47
+ * A function that returns the ending value for the interpolation. This is evaluated every frame if a function is provided.
48
+ */
49
+ smoothTimeMs: DynamicValue;
50
+ /**
51
+ * The precision threshold for determining when the interpolation has effectively reached its target. Defaults to 0.01.
52
+ */
53
+ precision?: number;
54
+ /**
55
+ * A callback function that is called on every update of the interpolation, receiving the current value and velocity as arguments.
56
+ */
57
+ onUpdate?: (value: number, velocity: number) => void;
58
+ /**
59
+ * A callback function that is called when the interpolation has effectively reached its target and is considered complete.
60
+ */
61
+ onEnded?: () => void;
62
+ /**
63
+ * An optional external ticker that can be provided to control the timing of the interpolation updates. If not provided, a default ticker will be used.
64
+ */
65
+ ticker?: ExternalTicker;
66
+ };
67
+ /**
68
+ * Options for configuring a smooth damped interpolation.
69
+ *
70
+ * Smooth damp progressively moves toward a target value with velocity that
71
+ * decreases as it approaches, producing a natural deceleration effect.
72
+ */
73
+ export type SmoothDampOptions = {
74
+ /**
75
+ * A function that returns the target value to smoothly move toward.
76
+ * This is evaluated every frame, allowing the target to change dynamically.
77
+ */
78
+ to: () => number;
79
+ /**
80
+ * The approximate time (in milliseconds) it takes for the value to settle
81
+ * at the target. Larger values produce slower, more gradual movement.
82
+ */
83
+ smoothTimeMs: DynamicValue;
84
+ /**
85
+ * An optional maximum speed cap (in units/second). Prevents excessively
86
+ * fast movement when the gap between current and target is large.
87
+ */
88
+ maxSpeed?: DynamicValue;
89
+ /**
90
+ * The precision threshold for determining when the interpolation has
91
+ * effectively reached its target. Defaults to 0.01.
92
+ */
93
+ precision?: number;
94
+ /**
95
+ * A callback function that is called on every update of the interpolation,
96
+ * receiving the current value and velocity as arguments.
97
+ */
98
+ onUpdate?: (value: number, velocity: number) => void;
99
+ /**
100
+ * A callback function that is called when the interpolation has effectively
101
+ * reached its target and is considered complete.
102
+ */
103
+ onEnded?: () => void;
104
+ /**
105
+ * An optional external ticker that can be provided to control the timing of
106
+ * the interpolation updates. If not provided, a default ticker will be used.
107
+ */
108
+ ticker?: ExternalTicker;
109
+ };
110
+ /**
111
+ * Options for configuring a spring-based interpolation.
112
+ *
113
+ * Spring physics simulate mass-spring-damper motion, producing bouncy or
114
+ * elastic movement toward the target value.
115
+ */
116
+ export type SpringOptions = {
117
+ /**
118
+ * A function that returns the target value toward which the spring pulls.
119
+ * This is evaluated every frame, allowing the target to change dynamically.
120
+ */
121
+ to: () => number;
122
+ /**
123
+ * The stiffness of the spring. Higher values produce faster, snappier motion.
124
+ * Defaults to 180.
125
+ */
126
+ stiffness?: DynamicValue;
127
+ /**
128
+ * The damping coefficient. Higher values reduce oscillation and settling time.
129
+ * Defaults to 12.
130
+ */
131
+ damping?: DynamicValue;
132
+ /**
133
+ * The mass of the simulated object. Higher values produce slower, heavier
134
+ * motion. Defaults to 1.
135
+ */
136
+ mass?: DynamicValue;
137
+ /**
138
+ * The precision threshold for determining when the spring has effectively
139
+ * settled at its target (both position and velocity within tolerance).
140
+ * Defaults to 0.01.
141
+ */
142
+ precision?: number;
143
+ /**
144
+ * A callback function that is called on every update of the spring,
145
+ * receiving the current value and velocity as arguments.
146
+ */
147
+ onUpdate?: (value: number, velocity: number) => void;
148
+ /**
149
+ * A callback function that is called when the spring has effectively
150
+ * settled at its target and is considered complete.
151
+ */
152
+ onEnded?: () => void;
153
+ /**
154
+ * An optional external ticker that can be provided to control the timing of
155
+ * the spring updates. If not provided, a default ticker will be used.
156
+ */
157
+ ticker?: ExternalTicker;
158
+ };
@@ -1,2 +1,15 @@
1
+ /**
2
+ * Represents a value that can either be a static number or a function
3
+ * that returns a number. Functions are re-evaluated each time the value
4
+ * is resolved, allowing dynamic values that change over time.
5
+ */
1
6
  export type DynamicValue = number | (() => number);
7
+ /**
8
+ * Resolves a {@link DynamicValue} to a concrete number.
9
+ * If the value is a function, it is called and its return value is used.
10
+ * If the value is a number, it is returned directly.
11
+ *
12
+ * @param value - The dynamic value to resolve.
13
+ * @returns The resolved number.
14
+ */
2
15
  export declare const resolveValue: (value: DynamicValue) => number;
@@ -1,4 +1,12 @@
1
1
  //#region src/domain/resolve-value.ts
2
+ /**
3
+ * Resolves a {@link DynamicValue} to a concrete number.
4
+ * If the value is a function, it is called and its return value is used.
5
+ * If the value is a number, it is returned directly.
6
+ *
7
+ * @param value - The dynamic value to resolve.
8
+ * @returns The resolved number.
9
+ */
2
10
  var resolveValue = (value) => typeof value === "function" ? value() : value;
3
11
  //#endregion
4
12
  export { resolveValue };
@@ -1,24 +1,36 @@
1
+ /**
2
+ * A function that is called on each tick with the elapsed time in milliseconds.
3
+ *
4
+ * @param deltaMs - The time elapsed since the last tick in milliseconds.
5
+ */
1
6
  export type TickHandler = (deltaMs: number) => void;
7
+ /**
8
+ * A ticker that drives frame updates. It manages a list of {@link TickHandler}
9
+ * callbacks and distributes time updates to them on each frame.
10
+ */
2
11
  export type Ticker = {
12
+ /** Starts the ticker, beginning to dispatch updates to registered handlers. */
3
13
  start: () => void;
14
+ /** Stops the ticker, halting further dispatch of updates. */
4
15
  stop: () => void;
16
+ /**
17
+ * Manually advances the ticker by the given delta, dispatching to all
18
+ * registered handlers.
19
+ */
5
20
  update: (deltaMs: number) => void;
21
+ /** Registers a handler to receive tick updates. */
6
22
  add: (handler: TickHandler) => void;
23
+ /** Unregisters a previously registered handler. */
7
24
  remove: (handler: TickHandler) => void;
8
25
  };
9
- /** Returns the default ticker singleton. Created lazily on first access. */
10
- export declare const getTicker: () => Ticker;
11
26
  /**
12
- * Create a ticker that drives active animations.
13
- *
14
- * Does NOT auto-start. The user must explicitly call either:
15
- * - `start()` — begins a `requestAnimationFrame` loop
16
- * - `update(deltaMs)` — drive manually from a game loop
17
- *
18
- * `add()` and `remove()` register/unregister animations without
19
- * side effects on the rAF loop.
20
- *
21
- * Uses a flat array with undefined-tombstone removal for safe concurrent
22
- * modification during iteration. Compacted after each frame.
27
+ * A minimal ticker interface exposing only subscription capabilities.
28
+ * Useful for external tickers (e.g. from a game loop or an animation
29
+ * framework) where start/stop/update are managed externally.
23
30
  */
24
- export declare const createTicker: () => Ticker;
31
+ export type ExternalTicker = {
32
+ /** Registers a handler to receive tick updates. */
33
+ add: (handler: TickHandler) => void;
34
+ /** Unregisters a previously registered handler. */
35
+ remove: (handler: TickHandler) => void;
36
+ };
@@ -1,5 +1,11 @@
1
- import { DynamicValue } from '../domain';
2
1
  import { KeyframeAnimationOptions, AnimationStatus } from './animation';
2
+ import { DynamicValue } from './resolve-value';
3
+ import { ExternalTicker } from './ticker';
4
+ /**
5
+ * A single layer in a timeline, describing when an animation plays.
6
+ * - `at`: The animation starts at this absolute point in time (in ms).
7
+ * - `gap`: The animation starts this many ms after the previous layer ends.
8
+ */
3
9
  export type TimelineLayer = {
4
10
  animation: KeyframeAnimationOptions;
5
11
  at: DynamicValue;
@@ -7,15 +13,54 @@ export type TimelineLayer = {
7
13
  animation: KeyframeAnimationOptions;
8
14
  gap: number;
9
15
  };
16
+ /**
17
+ * Callback hooks and options for a timeline animation.
18
+ */
19
+ export type TimelineCallbacks = {
20
+ /** Callback fired when the timeline starts playing. */
21
+ onStarted?: () => void;
22
+ /**
23
+ * Callback fired on each update with the current values and velocities
24
+ * of all layers in the timeline.
25
+ */
26
+ onUpdate?: (values: number[], velocities: number[]) => void;
27
+ /** Callback fired on each update with the current progress (0 to 1). */
28
+ onProgress?: (progress: number) => void;
29
+ /** Callback fired when the timeline ends. */
30
+ onEnded?: () => void;
31
+ /** Optional external ticker to drive the timeline. */
32
+ ticker?: ExternalTicker;
33
+ };
34
+ /**
35
+ * Represents a timeline animation instance that sequences multiple layers
36
+ * and can be controlled and queried for its aggregate state.
37
+ */
10
38
  export type Timeline = {
39
+ /** Plays the timeline from the current state. Returns a promise that resolves when the timeline ends. */
11
40
  play: () => Promise<void>;
41
+ /** Pauses the timeline if it is currently playing. */
12
42
  pause: () => void;
43
+ /** Resumes the timeline if it is currently paused. */
13
44
  resume: () => void;
45
+ /** Stops the timeline and resets its state. */
14
46
  stop: () => void;
47
+ /** Skips the timeline to its end state immediately. */
15
48
  skipToEnd: () => void;
16
- kill: () => void;
49
+ /** Sets the progress of the timeline to a specific value between 0 and 1. If playing, pauses. */
17
50
  setProgress: (value: number) => void;
51
+ /** The current progress of the timeline (0 to 1). */
18
52
  progress: number;
53
+ /**
54
+ * The current status of the timeline, which can be "playing", "paused", or "stopped".
55
+ * - "playing": The timeline is currently running.
56
+ * - "paused": The timeline is temporarily halted but can be resumed.
57
+ * - "stopped": The timeline has finished or has been stopped.
58
+ */
19
59
  status: AnimationStatus;
60
+ /** The total duration of the timeline in milliseconds. */
20
61
  durationMs: number;
62
+ /** The current values of all layers in the timeline. */
63
+ values: number[];
64
+ /** The current velocities of all layers in the timeline. */
65
+ velocities: number[];
21
66
  };
package/dist/index.d.ts CHANGED
@@ -1,13 +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, EASE_NAMES, cubicBezier } from './domain';
9
- export type { EaseName, Interpolation, AnimationStatus, InterpolationStatus, DynamicValue, Animation, Keyframe, AnimationOptions, KeyframeAnimationOptions, TimelineLayer, Timeline, Ticker, } from './domain';
10
- export type { SpringOptions } from './spring/create-spring';
11
- export type { SmoothDampOptions } from './smooth-damp/create-smooth-damp';
12
- export type { LerpOptions } from './lerp/create-lerp';
13
- export type { RgbaTuple } from './color/lerp-oklab';
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, ExternalTicker, } from './domain';
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
- import { EASE_NAMES, cubicBezier } from "./domain/easing.js";
2
- import { getTicker } from "./domain/ticker.js";
1
+ import { cubicBezier } from "./domain/easing.js";
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,14 @@
1
- import { Interpolation, DynamicValue } from '../domain';
2
- export type LerpOptions = {
3
- to: () => number;
4
- smoothTimeMs: DynamicValue;
5
- precision?: number;
6
- onUpdate?: (value: number, velocity: number) => void;
7
- onEnded?: () => void;
8
- };
9
- export declare const createLerp: (options: LerpOptions) => Interpolation;
1
+ import { Interpolation, LerpOptions } from '../domain';
2
+ /**
3
+ * Creates a linear interpolation that smoothly moves a value toward a
4
+ * target over a specified time constant. Uses exponential decay for a
5
+ * smooth, asymptotic approach.
6
+ *
7
+ * The interpolation automatically starts and runs until it reaches the
8
+ * target within the configured precision. The target is re-evaluated
9
+ * every frame, allowing it to change dynamically.
10
+ *
11
+ * @param options - Configuration options for the interpolation.
12
+ * @returns An {@link Interpolation} instance for controlling the lerp.
13
+ */
14
+ export declare const createLerp: ({ precision, onUpdate, onEnded, to, smoothTimeMs: rawSmoothTimeMs, ticker, }: LerpOptions) => Interpolation;
@@ -1,22 +1,30 @@
1
- import { getTicker } from "../domain/ticker.js";
2
1
  import { resolveValue } from "../domain/resolve-value.js";
2
+ import { getTicker } from "../ticker/get-ticker.js";
3
3
  import { lerpStep } from "./step.js";
4
4
  //#region src/lerp/create-lerp.ts
5
- var createLerp = (options) => {
6
- const precision = options.precision ?? .01;
7
- const onUpdate = options.onUpdate;
8
- const onEnded = options.onEnded;
5
+ /**
6
+ * Creates a linear interpolation that smoothly moves a value toward a
7
+ * target over a specified time constant. Uses exponential decay for a
8
+ * smooth, asymptotic approach.
9
+ *
10
+ * The interpolation automatically starts and runs until it reaches the
11
+ * target within the configured precision. The target is re-evaluated
12
+ * every frame, allowing it to change dynamically.
13
+ *
14
+ * @param options - Configuration options for the interpolation.
15
+ * @returns An {@link Interpolation} instance for controlling the lerp.
16
+ */
17
+ var createLerp = ({ precision = .01, onUpdate, onEnded, to, smoothTimeMs: rawSmoothTimeMs, ticker = getTicker() }) => {
9
18
  const state = { current: 0 };
10
19
  let previousValue = 0;
11
20
  let currentVelocity = 0;
12
21
  let active = true;
13
- const ticker = getTicker();
14
- state.current = options.to();
22
+ state.current = to();
15
23
  previousValue = state.current;
16
24
  const update = (deltaMs) => {
17
25
  if (!active) return;
18
- const target = options.to();
19
- const smoothTimeMs = resolveValue(options.smoothTimeMs);
26
+ const target = to();
27
+ const smoothTimeMs = resolveValue(rawSmoothTimeMs);
20
28
  lerpStep(state, target, smoothTimeMs, deltaMs);
21
29
  currentVelocity = (state.current - previousValue) / (deltaMs / 1e3);
22
30
  previousValue = state.current;
@@ -28,7 +36,7 @@ var createLerp = (options) => {
28
36
  }
29
37
  };
30
38
  ticker.add(update);
31
- const start = () => {
39
+ const resume = () => {
32
40
  if (active) return;
33
41
  active = true;
34
42
  ticker.add(update);
@@ -37,20 +45,15 @@ var createLerp = (options) => {
37
45
  active = false;
38
46
  ticker.remove(update);
39
47
  };
40
- const kill = () => {
41
- active = false;
42
- ticker.remove(update);
43
- };
44
48
  return {
45
- start,
49
+ resume,
46
50
  stop,
47
- kill,
48
- setCurrentValue: (value) => {
51
+ setValue: (value) => {
49
52
  state.current = value;
50
53
  previousValue = value;
51
54
  currentVelocity = 0;
52
55
  },
53
- get currentValue() {
56
+ get value() {
54
57
  return state.current;
55
58
  },
56
59
  get velocity() {
@@ -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,16 @@
1
- import { Interpolation, DynamicValue } from '../domain';
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
- };
10
- export declare const createSmoothDamp: (options: SmoothDampOptions) => Interpolation;
1
+ import { Interpolation, SmoothDampOptions } from '../domain';
2
+ /**
3
+ * Creates a smooth damped interpolation that progressively moves a value
4
+ * toward a target with velocity that decreases as it approaches, producing
5
+ * a natural deceleration effect.
6
+ *
7
+ * Unlike a simple lerp, smooth damp respects an optional max speed and
8
+ * maintains velocity continuity, making it suitable for camera-relative
9
+ * movement, UI animations, and game object tracking.
10
+ *
11
+ * The target is re-evaluated every frame, allowing it to change dynamically.
12
+ *
13
+ * @param options - Configuration options for the smooth damp interpolation.
14
+ * @returns An {@link Interpolation} instance for controlling the smooth damp.
15
+ */
16
+ export declare const createSmoothDamp: ({ to, smoothTimeMs: rawSmoothTimeMs, maxSpeed: rawMaxSpeed, precision, onUpdate, onEnded, ticker, }: SmoothDampOptions) => Interpolation;