@vune-ui/animation 0.1.20

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 (68) hide show
  1. package/ARCHITECTURE.md +470 -0
  2. package/CHANGELOG.md +88 -0
  3. package/LICENSE +21 -0
  4. package/PERFORMANCE.md +151 -0
  5. package/README.md +630 -0
  6. package/dist/index.d.ts +474 -0
  7. package/dist/src/canvas/index.d.ts +15 -0
  8. package/dist/src/canvas/index.js +67 -0
  9. package/dist/src/constraints/index.d.ts +33 -0
  10. package/dist/src/constraints/index.js +346 -0
  11. package/dist/src/core/bezier.js +51 -0
  12. package/dist/src/core/composition.js +17 -0
  13. package/dist/src/core/controls.js +22 -0
  14. package/dist/src/core/default-engine.js +20 -0
  15. package/dist/src/core/easing.js +58 -0
  16. package/dist/src/core/engine.js +1031 -0
  17. package/dist/src/core/frame-budget.js +30 -0
  18. package/dist/src/core/index.d.ts +43 -0
  19. package/dist/src/core/index.js +17 -0
  20. package/dist/src/core/js-spring-batch.js +57 -0
  21. package/dist/src/core/kinetics.js +140 -0
  22. package/dist/src/core/math.js +20 -0
  23. package/dist/src/core/motion-value.js +53 -0
  24. package/dist/src/core/planner.js +72 -0
  25. package/dist/src/core/specs.js +70 -0
  26. package/dist/src/dom/index.d.ts +41 -0
  27. package/dist/src/dom/index.js +364 -0
  28. package/dist/src/gesture/index.d.ts +66 -0
  29. package/dist/src/gesture/index.js +376 -0
  30. package/dist/src/index.js +53 -0
  31. package/dist/src/interpolate/color.js +223 -0
  32. package/dist/src/interpolate/css.d.ts +13 -0
  33. package/dist/src/interpolate/css.js +34 -0
  34. package/dist/src/interpolate/index.d.ts +13 -0
  35. package/dist/src/interpolate/index.js +55 -0
  36. package/dist/src/interpolate/transform.js +247 -0
  37. package/dist/src/layout/index.d.ts +56 -0
  38. package/dist/src/layout/index.js +485 -0
  39. package/dist/src/material/index.d.ts +9 -0
  40. package/dist/src/material/index.js +70 -0
  41. package/dist/src/path/index.d.ts +37 -0
  42. package/dist/src/path/index.js +527 -0
  43. package/dist/src/render/frame-batcher.js +52 -0
  44. package/dist/src/scroll/index.d.ts +55 -0
  45. package/dist/src/scroll/index.js +233 -0
  46. package/dist/src/timeline/index.d.ts +147 -0
  47. package/dist/src/timeline/index.js +849 -0
  48. package/dist/src/transition/index.d.ts +88 -0
  49. package/dist/src/transition/index.js +369 -0
  50. package/dist/src/wasm/index.d.ts +29 -0
  51. package/dist/src/wasm/index.js +8 -0
  52. package/dist/src/wasm/loader.js +55 -0
  53. package/dist/src/wasm/shared-wasm-spring-batch.js +52 -0
  54. package/dist/src/wasm/wasm-spring-batch.js +52 -0
  55. package/dist/src/webgl/index.d.ts +22 -0
  56. package/dist/src/webgl/index.js +94 -0
  57. package/dist/src/webgpu/index.d.ts +35 -0
  58. package/dist/src/webgpu/index.js +73 -0
  59. package/dist/src/webgpu/spring-batch.js +218 -0
  60. package/dist/src/worker/index.d.ts +17 -0
  61. package/dist/src/worker/index.js +1 -0
  62. package/dist/src/worker/shared-spring-worker.js +218 -0
  63. package/dist/src/worker/shared-worker.js +75 -0
  64. package/dist/wasm/kernel-scalar.wasm +0 -0
  65. package/dist/wasm/kernel-shared-scalar.wasm +0 -0
  66. package/dist/wasm/kernel-shared-simd.wasm +0 -0
  67. package/dist/wasm/kernel-simd.wasm +0 -0
  68. package/package.json +113 -0
@@ -0,0 +1,30 @@
1
+ import { FrameBudgetGovernor as ExecutionFrameBudgetGovernor } from '@vune-ui/execution';
2
+
3
+ // Backend threshold policy remains animation-owned. The sampling, EMA,
4
+ // pressure, and subscription signal are shared with other execution clients.
5
+ export class FrameBudgetGovernor extends ExecutionFrameBudgetGovernor {
6
+ constructor({
7
+ budgetMs = 8,
8
+ alpha = 0.12,
9
+ minWasmThreshold = 64,
10
+ minWorkerThreshold = 512,
11
+ } = {}) {
12
+ super({ budgetMs, alpha });
13
+ this.minWasmThreshold = Math.max(1, Math.floor(minWasmThreshold));
14
+ this.minWorkerThreshold = Math.max(this.minWasmThreshold, Math.floor(minWorkerThreshold));
15
+ }
16
+
17
+ wasmThreshold(baseThreshold, activeCount) {
18
+ const base = Math.max(1, Math.floor(baseThreshold));
19
+ if (this.pressure < 0.75 || activeCount < this.minWasmThreshold) return base;
20
+ const pressureScale = this.pressure >= 1.15 ? 0.35 : 0.6;
21
+ return Math.max(this.minWasmThreshold, Math.min(base, Math.floor(base * pressureScale)));
22
+ }
23
+
24
+ workerThreshold(baseThreshold, activeCount) {
25
+ const base = Math.max(1, Math.floor(baseThreshold));
26
+ if (this.pressure < 1 || activeCount < this.minWorkerThreshold) return base;
27
+ const pressureScale = this.pressure >= 1.35 ? 0.35 : 0.6;
28
+ return Math.max(this.minWorkerThreshold, Math.min(base, Math.floor(base * pressureScale)));
29
+ }
30
+ }
@@ -0,0 +1,43 @@
1
+ export {
2
+ MotionValue,
3
+ motionValue,
4
+ AnimationControls,
5
+ animate,
6
+ animateVelocity,
7
+ animateDecay,
8
+ animateInertia,
9
+ defaultEngine,
10
+ compileMotionPlan,
11
+ resolveMotionPlan,
12
+ isMotionExecutionPlan,
13
+ spring,
14
+ timing,
15
+ cubicBezier,
16
+ curves,
17
+ smooth,
18
+ snappy,
19
+ bouncy,
20
+ gentle,
21
+ interactive,
22
+ resolveMotionSpec,
23
+ compileEasing,
24
+ evaluateCompiledEasing,
25
+ derivativeCompiledEasing,
26
+ } from '../../index.js';
27
+ export type {
28
+ AnimationResult,
29
+ AnimationStatus,
30
+ MotionExecutionPlan,
31
+ ResolvedMotionExecutionPlan,
32
+ MotionSpec,
33
+ SpringSpec,
34
+ TimingSpec,
35
+ ProfileSpec,
36
+ DecayOptions,
37
+ DecaySpec,
38
+ InertiaOptions,
39
+ InertiaSpec,
40
+ VelocityAnimationSpec,
41
+ BezierCurve,
42
+ CompiledEasing,
43
+ } from '../../index.js';
@@ -0,0 +1,17 @@
1
+ export { MotionValue, motionValue } from './motion-value.js';
2
+ export { AnimationControls } from './controls.js';
3
+ export { animate, animateVelocity, animateDecay, animateInertia, defaultEngine } from './default-engine.js';
4
+ export { compileMotionPlan, resolveMotionPlan, isMotionExecutionPlan } from './planner.js';
5
+ export {
6
+ spring,
7
+ timing,
8
+ cubicBezier,
9
+ curves,
10
+ smooth,
11
+ snappy,
12
+ bouncy,
13
+ gentle,
14
+ interactive,
15
+ resolveMotionSpec,
16
+ } from './specs.js';
17
+ export { compileEasing, evaluateCompiledEasing, derivativeCompiledEasing } from './easing.js';
@@ -0,0 +1,57 @@
1
+ const MAX_STEP_SECONDS = 1 / 240;
2
+ const MAX_SUBSTEPS = 32;
3
+
4
+ export class JsSpringBatch {
5
+ constructor(capacity = 256) {
6
+ this.kind = 'js';
7
+ this.capacity = capacity;
8
+ this.positions = new Float32Array(capacity);
9
+ this.velocities = new Float32Array(capacity);
10
+ this.targets = new Float32Array(capacity);
11
+ this.omegas = new Float32Array(capacity);
12
+ this.dampingRatios = new Float32Array(capacity);
13
+ }
14
+
15
+ ensureCapacity(required) {
16
+ if (required <= this.capacity) return;
17
+ let next = this.capacity;
18
+ while (next < required) next *= 2;
19
+ for (const key of ['positions', 'velocities', 'targets', 'omegas', 'dampingRatios']) {
20
+ const old = this[key];
21
+ const replacement = new Float32Array(next);
22
+ replacement.set(old);
23
+ this[key] = replacement;
24
+ }
25
+ this.capacity = next;
26
+ }
27
+
28
+ copyInto(other, count) {
29
+ other.positions.set(this.positions.subarray(0, count));
30
+ other.velocities.set(this.velocities.subarray(0, count));
31
+ other.targets.set(this.targets.subarray(0, count));
32
+ other.omegas.set(this.omegas.subarray(0, count));
33
+ other.dampingRatios.set(this.dampingRatios.subarray(0, count));
34
+ }
35
+
36
+ step(count, dtSeconds) {
37
+ if (count === 0 || dtSeconds <= 0) return;
38
+ let steps = 1;
39
+ while (dtSeconds / steps > MAX_STEP_SECONDS && steps < MAX_SUBSTEPS) steps += 1;
40
+ const h = dtSeconds / steps;
41
+
42
+ for (let s = 0; s < steps; s += 1) {
43
+ for (let i = 0; i < count; i += 1) {
44
+ let x = this.positions[i];
45
+ let v = this.velocities[i];
46
+ const target = this.targets[i];
47
+ const omega = this.omegas[i];
48
+ const zeta = this.dampingRatios[i];
49
+ const acceleration = omega * omega * (target - x) - 2 * zeta * omega * v;
50
+ v += acceleration * h;
51
+ x += v * h;
52
+ this.positions[i] = x;
53
+ this.velocities[i] = v;
54
+ }
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,140 @@
1
+ import { clamp, springParamsFromResponse } from './math.js';
2
+
3
+ const DEFAULT_TAU = 0.325;
4
+ const DEFAULT_REST_SPEED = 5;
5
+ const DEFAULT_REST_DELTA = 0.5;
6
+
7
+ function finiteOr(value, fallback) {
8
+ return Number.isFinite(value) ? Number(value) : fallback;
9
+ }
10
+
11
+ function optionalBound(value, fallback) {
12
+ return Number.isFinite(value) ? Number(value) : fallback;
13
+ }
14
+
15
+ export function decay(options = {}) {
16
+ return Object.freeze({
17
+ kind: 'decay',
18
+ velocity: Number.isFinite(options.velocity) ? Number(options.velocity) : undefined,
19
+ timeConstant: Math.max(0.016, finiteOr(options.timeConstant, DEFAULT_TAU)),
20
+ power: Math.max(0, finiteOr(options.power, 1)),
21
+ restSpeed: Math.max(0, finiteOr(options.restSpeed, DEFAULT_REST_SPEED)),
22
+ modifyTarget: typeof options.modifyTarget === 'function' ? options.modifyTarget : undefined,
23
+ });
24
+ }
25
+
26
+ export function inertia(options = {}) {
27
+ const bounce = options.bounce && options.bounce.kind === 'spring'
28
+ ? options.bounce
29
+ : { kind: 'spring', ...springParamsFromResponse(
30
+ finiteOr(options.bounceResponse, 0.28),
31
+ finiteOr(options.bounceDampingRatio, 0.82),
32
+ ) };
33
+
34
+ const min = optionalBound(options.min, -Infinity);
35
+ const max = optionalBound(options.max, Infinity);
36
+ if (min > max) throw new RangeError('inertia() min cannot be greater than max.');
37
+
38
+ return Object.freeze({
39
+ kind: 'inertia',
40
+ velocity: Number.isFinite(options.velocity) ? Number(options.velocity) : undefined,
41
+ timeConstant: Math.max(0.016, finiteOr(options.timeConstant, DEFAULT_TAU)),
42
+ power: Math.max(0, finiteOr(options.power, 0.8)),
43
+ restSpeed: Math.max(0, finiteOr(options.restSpeed, DEFAULT_REST_SPEED)),
44
+ restDelta: Math.max(0, finiteOr(options.restDelta, DEFAULT_REST_DELTA)),
45
+ min,
46
+ max,
47
+ bounceOmega: bounce.omega,
48
+ bounceDampingRatio: bounce.dampingRatio,
49
+ modifyTarget: typeof options.modifyTarget === 'function' ? options.modifyTarget : undefined,
50
+ });
51
+ }
52
+
53
+ export function projectDecayTarget(value, velocity, spec) {
54
+ const base = value + velocity * spec.timeConstant * spec.power;
55
+ const modified = spec.modifyTarget ? Number(spec.modifyTarget(base)) : base;
56
+ return Number.isFinite(modified) ? modified : base;
57
+ }
58
+
59
+ export function nearestBound(value, min = -Infinity, max = Infinity) {
60
+ if (value < min) return min;
61
+ if (value > max) return max;
62
+ return null;
63
+ }
64
+
65
+ export function clampToBounds(value, min = -Infinity, max = Infinity) {
66
+ if (!Number.isFinite(min) && !Number.isFinite(max)) return value;
67
+ return clamp(value, min, max);
68
+ }
69
+
70
+ // Exact exponential integration for dv/dt = -v/tau. This is frame-rate
71
+ // independent and lands asymptotically at x + v*tau.
72
+ export function stepDecay(position, velocity, dtSeconds, timeConstant, out = undefined) {
73
+ const result = out ?? { position: 0, velocity: 0 };
74
+ if (!(dtSeconds > 0) || !(timeConstant > 0)) {
75
+ result.position = position;
76
+ result.velocity = velocity;
77
+ return result;
78
+ }
79
+ const attenuation = Math.exp(-dtSeconds / timeConstant);
80
+ result.position = position + velocity * timeConstant * (1 - attenuation);
81
+ result.velocity = velocity * attenuation;
82
+ return result;
83
+ }
84
+
85
+ // Exact solution of y'' + 2*zeta*omega*y' + omega^2*y = 0 around target.
86
+ // Used for low-count interaction settling so it remains stable across long or
87
+ // irregular pointer frames without sub-stepping.
88
+ export function stepDampedSpring(position, velocity, target, omega, dampingRatio, dtSeconds, out = undefined) {
89
+ const result = out ?? { position: 0, velocity: 0 };
90
+ if (!(dtSeconds > 0) || !(omega > 0)) {
91
+ result.position = position;
92
+ result.velocity = velocity;
93
+ return result;
94
+ }
95
+ const y0 = position - target;
96
+ const v0 = velocity;
97
+ const zeta = Math.max(0, dampingRatio);
98
+ const criticalEpsilon = 1e-4;
99
+
100
+ if (zeta < 1 - criticalEpsilon) {
101
+ const alpha = zeta * omega;
102
+ const wd = omega * Math.sqrt(1 - zeta * zeta);
103
+ const exp = Math.exp(-alpha * dtSeconds);
104
+ const sin = Math.sin(wd * dtSeconds);
105
+ const cos = Math.cos(wd * dtSeconds);
106
+ const b = (v0 + alpha * y0) / wd;
107
+ const y = exp * (y0 * cos + b * sin);
108
+ const v = exp * (
109
+ -alpha * (y0 * cos + b * sin)
110
+ + (-y0 * wd * sin + b * wd * cos)
111
+ );
112
+ result.position = target + y;
113
+ result.velocity = v;
114
+ return result;
115
+ }
116
+
117
+ if (zeta > 1 + criticalEpsilon) {
118
+ const root = Math.sqrt(zeta * zeta - 1);
119
+ const r1 = -omega * (zeta - root);
120
+ const r2 = -omega * (zeta + root);
121
+ const denominator = r1 - r2;
122
+ const c1 = (v0 - r2 * y0) / denominator;
123
+ const c2 = y0 - c1;
124
+ const e1 = Math.exp(r1 * dtSeconds);
125
+ const e2 = Math.exp(r2 * dtSeconds);
126
+ const y = c1 * e1 + c2 * e2;
127
+ const v = c1 * r1 * e1 + c2 * r2 * e2;
128
+ result.position = target + y;
129
+ result.velocity = v;
130
+ return result;
131
+ }
132
+
133
+ const exp = Math.exp(-omega * dtSeconds);
134
+ const b = v0 + omega * y0;
135
+ const y = exp * (y0 + b * dtSeconds);
136
+ const v = exp * (b - omega * (y0 + b * dtSeconds));
137
+ result.position = target + y;
138
+ result.velocity = v;
139
+ return result;
140
+ }
@@ -0,0 +1,20 @@
1
+ export const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
2
+
3
+ export function springParamsFromResponse(response = 0.38, dampingRatio = 0.82) {
4
+ const safeResponse = Math.max(0.05, Number(response) || 0.38);
5
+ return {
6
+ omega: (Math.PI * 2) / safeResponse,
7
+ dampingRatio: Math.max(0, Number(dampingRatio) || 0),
8
+ };
9
+ }
10
+
11
+ export function springParamsFromPhysics({ mass = 1, stiffness = 170, damping = 18 } = {}) {
12
+ const m = Math.max(1e-6, mass);
13
+ const k = Math.max(1e-6, stiffness);
14
+ const c = Math.max(0, damping);
15
+ const omega = Math.sqrt(k / m);
16
+ return {
17
+ omega,
18
+ dampingRatio: c / (2 * Math.sqrt(k * m)),
19
+ };
20
+ }
@@ -0,0 +1,53 @@
1
+ export class MotionValue {
2
+ #value;
3
+ #velocity = 0;
4
+ #listeners = new Set();
5
+ #valueListeners = new Set();
6
+ #version = 0;
7
+
8
+ constructor(initial = 0) {
9
+ if (!Number.isFinite(initial)) throw new TypeError('MotionValue requires a finite number.');
10
+ this.#value = initial;
11
+ }
12
+
13
+ get() { return this.#value; }
14
+ getVelocity() { return this.#velocity; }
15
+ getVersion() { return this.#version; }
16
+
17
+ set(value, velocity = 0) {
18
+ if (!Number.isFinite(value)) return;
19
+ this.#commit(value, velocity);
20
+ }
21
+
22
+ #commit(value, velocity) {
23
+ if (Object.is(value, this.#value) && Object.is(velocity, this.#velocity)) return;
24
+ const previous = this.#value;
25
+ this.#value = value;
26
+ this.#velocity = velocity;
27
+ this.#version += 1;
28
+
29
+ for (const listener of this.#valueListeners) listener(value);
30
+ if (this.#listeners.size > 0) {
31
+ const info = { previous, velocity, version: this.#version };
32
+ for (const listener of this.#listeners) listener(value, info);
33
+ }
34
+ }
35
+
36
+ _commit(value, velocity) { this.#commit(value, velocity); }
37
+
38
+ subscribe(listener, { emitCurrent = true } = {}) {
39
+ this.#listeners.add(listener);
40
+ if (emitCurrent) listener(this.#value, { previous: this.#value, velocity: this.#velocity, version: this.#version });
41
+ return () => this.#listeners.delete(listener);
42
+ }
43
+
44
+ subscribeValue(listener, { emitCurrent = true } = {}) {
45
+ this.#valueListeners.add(listener);
46
+ if (emitCurrent) listener(this.#value);
47
+ return () => this.#valueListeners.delete(listener);
48
+ }
49
+ }
50
+
51
+ export function motionValue(initial = 0) {
52
+ return new MotionValue(initial);
53
+ }
@@ -0,0 +1,72 @@
1
+ import { compileEasing } from './easing.js';
2
+ import { resolveMotionSpec, smooth } from './specs.js';
3
+
4
+ const compiledPlans = new WeakMap();
5
+ const resolvedProfilePlans = new WeakMap();
6
+ const maximumResolvedProfilePlans = 64;
7
+ const defaultProfile = smooth();
8
+
9
+ function freezePlan(plan) {
10
+ return Object.freeze({ kind: 'motion-plan', ...plan });
11
+ }
12
+
13
+ function compileResolvedSpec(spec) {
14
+ if (!spec || typeof spec !== 'object') throw new TypeError('Motion spec must be an object.');
15
+ if (spec.kind === 'spring') {
16
+ return freezePlan({
17
+ route: 'spring',
18
+ spec,
19
+ omega: spec.omega,
20
+ dampingRatio: spec.dampingRatio,
21
+ initialVelocity: spec.initialVelocity,
22
+ blendDurationMs: Math.max(0, Number(spec.blendDuration) || 0) * 1000,
23
+ });
24
+ }
25
+ if (spec.kind === 'timing') {
26
+ return freezePlan({
27
+ route: 'timing',
28
+ spec,
29
+ durationMs: Math.max(0, spec.duration) * 1000,
30
+ easing: compileEasing(spec.curve),
31
+ });
32
+ }
33
+ if (spec.kind === 'profile') return freezePlan({ route: 'profile', spec });
34
+ throw new TypeError(`Unknown motion spec kind: ${String(spec.kind)}`);
35
+ }
36
+
37
+ export function isMotionExecutionPlan(value) {
38
+ return Boolean(value && typeof value === 'object' && value.kind === 'motion-plan');
39
+ }
40
+
41
+ /**
42
+ * Compile the invariant half of a motion request once. Spring coefficients and
43
+ * timing easing tables are frozen into the returned plan; adaptive profiles
44
+ * intentionally retain a tiny dynamic resolver because their response depends
45
+ * on the current travel distance.
46
+ */
47
+ export function compileMotionPlan(requestedSpec) {
48
+ if (isMotionExecutionPlan(requestedSpec)) return requestedSpec;
49
+ const spec = requestedSpec ?? defaultProfile;
50
+ if (!spec || typeof spec !== 'object') throw new TypeError('Motion spec must be an object.');
51
+ const cached = compiledPlans.get(spec);
52
+ if (cached) return cached;
53
+ const plan = compileResolvedSpec(spec);
54
+ compiledPlans.set(spec, plan);
55
+ return plan;
56
+ }
57
+
58
+ /** Resolve only the distance-sensitive portion of a compiled plan. */
59
+ export function resolveMotionPlan(requestedPlan, from, to) {
60
+ const plan = compileMotionPlan(requestedPlan);
61
+ if (plan.route !== 'profile') return plan;
62
+ const distance = Math.abs(to - from);
63
+ let cache = resolvedProfilePlans.get(plan.spec);
64
+ if (!cache) { cache = new Map(); resolvedProfilePlans.set(plan.spec, cache); }
65
+ const cached = cache.get(distance);
66
+ if (cached) { cache.delete(distance); cache.set(distance, cached); return cached; }
67
+ const resolved = resolveMotionSpec(plan.spec, from, to);
68
+ const compiled = compileResolvedSpec(resolved);
69
+ cache.set(distance, compiled);
70
+ while (cache.size > maximumResolvedProfilePlans) cache.delete(cache.keys().next().value);
71
+ return compiled;
72
+ }
@@ -0,0 +1,70 @@
1
+ import { clamp, springParamsFromPhysics, springParamsFromResponse } from './math.js';
2
+
3
+ export function spring(options = {}) {
4
+ const { response = 0.38, dampingRatio = 0.82, initialVelocity, blendDuration = 0 } = options;
5
+ const params = springParamsFromResponse(response, dampingRatio);
6
+ return Object.freeze({ kind: 'spring', ...params, initialVelocity, blendDuration: Math.max(0, Number(blendDuration) || 0), source: 'response' });
7
+ }
8
+
9
+ spring.physics = function physics(options = {}) {
10
+ const params = springParamsFromPhysics(options);
11
+ return Object.freeze({
12
+ kind: 'spring',
13
+ ...params,
14
+ initialVelocity: options.initialVelocity,
15
+ blendDuration: Math.max(0, Number(options.blendDuration) || 0),
16
+ source: 'physics',
17
+ });
18
+ };
19
+
20
+ export function cubicBezier(x1, y1, x2, y2) {
21
+ return Object.freeze({ kind: 'bezier', x1, y1, x2, y2 });
22
+ }
23
+
24
+ export const curves = Object.freeze({
25
+ linear: cubicBezier(0, 0, 1, 1),
26
+ easeIn: cubicBezier(0.42, 0, 1, 1),
27
+ easeOut: cubicBezier(0, 0, 0.58, 1),
28
+ easeInOut: cubicBezier(0.42, 0, 0.58, 1),
29
+ smooth: cubicBezier(0.22, 1, 0.36, 1),
30
+ });
31
+
32
+ export function timing({ duration = 0.3, curve = curves.easeInOut } = {}) {
33
+ return Object.freeze({
34
+ kind: 'timing',
35
+ duration: Math.max(0, duration),
36
+ curve,
37
+ });
38
+ }
39
+
40
+ function profile(name, options = {}) {
41
+ return Object.freeze({ kind: 'profile', name, options: Object.freeze({ ...options }) });
42
+ }
43
+
44
+ export const smooth = (options) => profile('smooth', options);
45
+ export const snappy = (options) => profile('snappy', options);
46
+ export const bouncy = (options) => profile('bouncy', options);
47
+ export const gentle = (options) => profile('gentle', options);
48
+ export const interactive = (options) => profile('interactive', options);
49
+
50
+ export function resolveMotionSpec(spec, from, to) {
51
+ if (!spec || spec.kind !== 'profile') return spec || smooth();
52
+
53
+ const distance = Math.abs(to - from);
54
+ const normalized = clamp(Math.log1p(distance) / Math.log1p(1000), 0, 1);
55
+ const bias = Number(spec.options?.responseBias || 0);
56
+
57
+ switch (spec.name) {
58
+ case 'snappy':
59
+ return spring({ response: 0.19 + normalized * 0.08 + bias, dampingRatio: 0.88 });
60
+ case 'bouncy':
61
+ return spring({ response: 0.31 + normalized * 0.09 + bias, dampingRatio: 0.67 });
62
+ case 'gentle':
63
+ return spring({ response: 0.43 + normalized * 0.12 + bias, dampingRatio: 0.98 });
64
+ case 'interactive':
65
+ return spring({ response: 0.20 + normalized * 0.09 + bias, dampingRatio: 0.84 });
66
+ case 'smooth':
67
+ default:
68
+ return spring({ response: 0.26 + normalized * 0.16 + bias, dampingRatio: 0.92 });
69
+ }
70
+ }
@@ -0,0 +1,41 @@
1
+ import type {
2
+ AnimationControls,
3
+ MotionSpec,
4
+ MotionValue,
5
+ MotionEngine,
6
+ InterpolatorOptions,
7
+ MaterialInput,
8
+ ColorSpace,
9
+ PathMorphOptions,
10
+ } from '../../index.js';
11
+ export function configureDomBatching(options?: { scheduler?: 'microtask' | 'raf' }): void;
12
+ export function flushDomCommits(): number;
13
+ export function bindMotionStyles(element: HTMLElement, bindings: Record<string, MotionValue>): () => void;
14
+ export function bindStyleValue(element: HTMLElement, property: string, motion: MotionValue, options?: { unit?: string }): () => void;
15
+ export function animateStyle(element: HTMLElement, property: string, from: unknown, to: unknown, spec?: MotionSpec, options?: InterpolatorOptions & { engine?: MotionEngine }): AnimationControls;
16
+ export function cancelStyleAnimations(element: Element, properties?: string | string[]): number;
17
+ export function ownStyleAnimation<T extends { cancel(): void; readonly finished: Promise<unknown> }>(element: Element, properties: string | string[], control: T): T;
18
+ export function animateStyleOwned(element: HTMLElement, property: string, from: unknown, to: unknown, spec?: MotionSpec, options?: InterpolatorOptions & { engine?: MotionEngine }): AnimationControls;
19
+ export function applyMaterial(element: HTMLElement, material: MaterialInput, options?: { background?: boolean }): void;
20
+ export function animateMaterial(element: HTMLElement, from: MaterialInput, to: MaterialInput, spec?: MotionSpec, options?: { background?: boolean; colorSpace?: ColorSpace; engine?: MotionEngine }): AnimationControls;
21
+ export function animateAttribute(element: Element, name: string, from: unknown, to: unknown, spec?: MotionSpec, options?: InterpolatorOptions & { engine?: MotionEngine }): AnimationControls;
22
+ export function animatePath(element: Element, from: string, to: string, spec?: MotionSpec, options?: PathMorphOptions & { engine?: MotionEngine }): AnimationControls;
23
+ export function animateNative(element: Element, keyframes: Keyframe[] | PropertyIndexedKeyframes, options?: { duration?: number; easing?: string; fill?: FillMode }): Animation;
24
+
25
+ export function bindPointerDrag(
26
+ element: HTMLElement,
27
+ controller: {
28
+ start(point: { x: number; y: number }, time?: number): unknown;
29
+ move(point: { x: number; y: number }, time?: number): unknown;
30
+ end(time?: number): unknown;
31
+ cancel(options?: { settle?: boolean }): unknown;
32
+ },
33
+ options?: {
34
+ button?: number;
35
+ pointerCapture?: boolean;
36
+ preventDefault?: boolean;
37
+ touchAction?: string | null;
38
+ coalesced?: boolean;
39
+ filter?: (event: PointerEvent) => boolean;
40
+ },
41
+ ): () => void;