@vishinvents/aerostat 0.1.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/LICENSE +21 -0
- package/README.md +260 -0
- package/dist/animate-B3pY7b4F.d.ts +332 -0
- package/dist/chunk-GZKQYUY7.js +14 -0
- package/dist/chunk-GZKQYUY7.js.map +1 -0
- package/dist/chunk-Q5ABXDLW.js +2 -0
- package/dist/chunk-Q5ABXDLW.js.map +1 -0
- package/dist/chunk-W7GGS6ZY.js +2 -0
- package/dist/chunk-W7GGS6ZY.js.map +1 -0
- package/dist/core/index.d.ts +245 -0
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/pulse-D0zYT9Op.d.ts +151 -0
- package/dist/react.d.ts +110 -0
- package/dist/react.js +2 -0
- package/dist/react.js.map +1 -0
- package/dist/solid.d.ts +116 -0
- package/dist/solid.js +2 -0
- package/dist/solid.js.map +1 -0
- package/dist/svelte.d.ts +132 -0
- package/dist/svelte.js +2 -0
- package/dist/svelte.js.map +1 -0
- package/dist/ui/svelte/index.d.ts +257 -0
- package/dist/ui/svelte/index.js +28 -0
- package/dist/ui/svelte/index.js.map +1 -0
- package/package.json +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aerostat Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# Aerostat
|
|
2
|
+
|
|
3
|
+
**The missing physics the browser forgot to give you.**
|
|
4
|
+
|
|
5
|
+
~3KB • Zero dependencies • Spring-first motion
|
|
6
|
+
|
|
7
|
+
Aerostat is the animation primitive for modern frameworks. While Svelte, React, and Solid handle the DOM, Aerostat handles **time and physics** — with automatic interrupt handling, velocity catching, and a unified spring engine.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Why Aerostat?
|
|
12
|
+
|
|
13
|
+
| Feature | Aerostat | CSS/Web Animations | GSAP |
|
|
14
|
+
|---------|----------|-------------------|------|
|
|
15
|
+
| **Size** | ~3KB | 0KB | ~60KB |
|
|
16
|
+
| **Spring Physics** | Native | Needs JS | Plugin |
|
|
17
|
+
| **Interrupt Handling** | Automatic (WeakMap) | Manual | Manual |
|
|
18
|
+
| **Velocity Catching** | Built-in | None | Manual |
|
|
19
|
+
| **Modern Framework DX** | Svelte actions, React hooks | Generic | Generic |
|
|
20
|
+
|
|
21
|
+
**Optimized for the Modern Stack** — where the framework owns the DOM and you need physics-based motion without the bloat.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- 🎯 **Singleton Scheduler** - Single global rAF loop prevents layout thrashing
|
|
28
|
+
- ⚡️ **Spring Physics** - Realistic damped harmonic oscillator with velocity catching
|
|
29
|
+
- 🔄 **Automatic Interrupts** - WeakMap-based collision detection for smooth transitions
|
|
30
|
+
- 🎨 **Framework Adapters** - Tree-shakeable actions/hooks for Svelte, React, SolidJS
|
|
31
|
+
- 📦 **Tiny Bundle** - Core library under 3KB gzipped
|
|
32
|
+
- 🛠️ **TypeScript** - Full type safety with strict mode
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install aerostat
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
### Vanilla JS / TypeScript
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { aerostat } from 'aerostat';
|
|
50
|
+
|
|
51
|
+
// Spring animation (default)
|
|
52
|
+
aerostat({
|
|
53
|
+
from: 0,
|
|
54
|
+
to: 100,
|
|
55
|
+
onUpdate: (value) => {
|
|
56
|
+
element.style.opacity = value / 100;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Duration-based with easing
|
|
61
|
+
aerostat({
|
|
62
|
+
from: 0,
|
|
63
|
+
to: 100,
|
|
64
|
+
type: 'duration',
|
|
65
|
+
duration: 300,
|
|
66
|
+
easing: easeOutExpo,
|
|
67
|
+
onUpdate: (value) => {
|
|
68
|
+
element.style.transform = `translateX(${value}px)`;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Svelte
|
|
74
|
+
|
|
75
|
+
```svelte
|
|
76
|
+
<script>
|
|
77
|
+
import { squish } from 'aerostat/svelte';
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<button use:squish>Click me</button>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### React / Next.js
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { createUseAnimate } from 'aerostat/react';
|
|
87
|
+
import * as React from 'react';
|
|
88
|
+
|
|
89
|
+
const useAnimate = createUseAnimate(React);
|
|
90
|
+
|
|
91
|
+
function Component() {
|
|
92
|
+
const [opacity, setOpacity] = React.useState(0);
|
|
93
|
+
|
|
94
|
+
useAnimate({
|
|
95
|
+
from: 0,
|
|
96
|
+
to: 1,
|
|
97
|
+
duration: 300,
|
|
98
|
+
onUpdate: setOpacity
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return <div style={{ opacity }}>Fading in...</div>;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### SolidJS
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
import { createReactiveAnimation } from 'aerostat/solid';
|
|
109
|
+
import { createSignal, createEffect, onCleanup } from 'solid-js';
|
|
110
|
+
|
|
111
|
+
function Component() {
|
|
112
|
+
const [value, setValue] = createSignal(0);
|
|
113
|
+
|
|
114
|
+
createEffect(() => {
|
|
115
|
+
const controller = createReactiveAnimation({
|
|
116
|
+
from: 0,
|
|
117
|
+
to: 100,
|
|
118
|
+
onUpdate: setValue
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
onCleanup(() => controller.stop());
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return <div>{value()}</div>;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Documentation
|
|
131
|
+
|
|
132
|
+
- [Core API Reference](./docs/API.md) - Main animation functions and types
|
|
133
|
+
- [Framework Adapters](./docs/FRAMEWORKS.md) - Svelte, React, and SolidJS integrations
|
|
134
|
+
- [Examples & Patterns](./docs/EXAMPLES.md) - Common use cases and recipes
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Core Concepts
|
|
139
|
+
|
|
140
|
+
### Spring Physics
|
|
141
|
+
|
|
142
|
+
Springs create natural, momentum-based animations:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
aerostat({
|
|
146
|
+
from: 0,
|
|
147
|
+
to: 100,
|
|
148
|
+
stiffness: 170, // Higher = faster
|
|
149
|
+
damping: 12, // Lower = more bounce
|
|
150
|
+
onUpdate: (v) => element.style.left = `${v}px`
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Automatic Interrupts
|
|
155
|
+
|
|
156
|
+
Calling a new animation on the same target automatically stops the previous one:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const controller = aerostat({
|
|
160
|
+
from: 0,
|
|
161
|
+
to: 100,
|
|
162
|
+
onUpdate: (v) => element.style.opacity = v / 100
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Later - this will stop the first animation
|
|
166
|
+
aerostat({
|
|
167
|
+
from: controller.getValue(),
|
|
168
|
+
to: 0,
|
|
169
|
+
velocity: controller.getVelocity(), // Catch momentum!
|
|
170
|
+
onUpdate: (v) => element.style.opacity = v / 100
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Key-Based Registry
|
|
175
|
+
|
|
176
|
+
Name animations to interrupt by key:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
aerostat({
|
|
180
|
+
key: 'my-animation',
|
|
181
|
+
from: 0,
|
|
182
|
+
to: 100,
|
|
183
|
+
onUpdate: (v) => element.style.left = `${v}px`
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Later - stops 'my-animation'
|
|
187
|
+
aerostat({
|
|
188
|
+
key: 'my-animation',
|
|
189
|
+
from: 100,
|
|
190
|
+
to: 200,
|
|
191
|
+
onUpdate: (v) => element.style.left = `${v}px`
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Spring Presets
|
|
198
|
+
|
|
199
|
+
Four built-in presets tuned for common use cases:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { snappy, bouncy, smooth, heavy } from 'aerostat';
|
|
203
|
+
|
|
204
|
+
// Use directly
|
|
205
|
+
aerostat({ from: 0, to: 100, ...snappy, onUpdate });
|
|
206
|
+
|
|
207
|
+
// Or set globally
|
|
208
|
+
import { setDefaultPreset } from 'aerostat';
|
|
209
|
+
setDefaultPreset('snappy'); // All animations now use snappy
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
| Preset | Stiffness | Damping | Feel |
|
|
213
|
+
|--------|-----------|---------|------|
|
|
214
|
+
| `snappy` | 400 | 30 | Quick, responsive, minimal overshoot |
|
|
215
|
+
| `bouncy` | 180 | 12 | Energetic, playful overshoot |
|
|
216
|
+
| `smooth` | 120 | 20 | Gentle, elegant, no overshoot |
|
|
217
|
+
| `heavy` | 100 | 18 | Weighty, deliberate, slow settle |
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Bundle Size
|
|
222
|
+
|
|
223
|
+
| Import | Size (gzipped) |
|
|
224
|
+
|--------|---------------|
|
|
225
|
+
| Core (`aerostat`) | ~2.9 KB |
|
|
226
|
+
| Svelte adapter | +402 B |
|
|
227
|
+
| React adapter | +459 B |
|
|
228
|
+
| SolidJS adapter | +426 B |
|
|
229
|
+
| Squish utility | +500 B |
|
|
230
|
+
|
|
231
|
+
Tree-shakeable - only bundle what you import.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Browser Support
|
|
236
|
+
|
|
237
|
+
- Chrome/Edge 90+
|
|
238
|
+
- Firefox 88+
|
|
239
|
+
- Safari 14+
|
|
240
|
+
- All modern mobile browsers
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Contributing
|
|
245
|
+
|
|
246
|
+
PRs welcome! Please open an issue first to discuss what you'd like to change.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT © 2026
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Credits
|
|
257
|
+
|
|
258
|
+
Built with ❤️ for the micro-interaction community.
|
|
259
|
+
|
|
260
|
+
Inspired by the physics of real-world motion and the elegance of minimal APIs.
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/** Available preset names */
|
|
2
|
+
type PresetName = 'snappy' | 'bouncy' | 'smooth' | 'heavy';
|
|
3
|
+
/** Preset configuration (read-only) */
|
|
4
|
+
type PresetConfig = Readonly<Required<SpringConfig>>;
|
|
5
|
+
/**
|
|
6
|
+
* Snappy - Quick, responsive, minimal overshoot.
|
|
7
|
+
* Best for: UI state changes, toggles, micro-interactions.
|
|
8
|
+
* Feel: "Instant but alive"
|
|
9
|
+
*/
|
|
10
|
+
declare const PRESET_SNAPPY: PresetConfig;
|
|
11
|
+
/**
|
|
12
|
+
* Bouncy - Energetic, playful overshoot.
|
|
13
|
+
* Best for: Success states, notifications, attention-grabbers.
|
|
14
|
+
* Feel: "Pop and settle"
|
|
15
|
+
*/
|
|
16
|
+
declare const PRESET_BOUNCY: PresetConfig;
|
|
17
|
+
/**
|
|
18
|
+
* Smooth - Gentle, elegant, no overshoot.
|
|
19
|
+
* Best for: Page transitions, modals, content reveals.
|
|
20
|
+
* Feel: "Glide into place"
|
|
21
|
+
*/
|
|
22
|
+
declare const PRESET_SMOOTH: PresetConfig;
|
|
23
|
+
/**
|
|
24
|
+
* Heavy - Weighty, deliberate, slow settle.
|
|
25
|
+
* Best for: Large elements, dramatic reveals, emphasis.
|
|
26
|
+
* Feel: "Massive and powerful"
|
|
27
|
+
*/
|
|
28
|
+
declare const PRESET_HEAVY: PresetConfig;
|
|
29
|
+
/**
|
|
30
|
+
* All presets as a lookup object.
|
|
31
|
+
* Use individual constants (PRESET_SNAPPY, etc.) for best tree-shaking.
|
|
32
|
+
*/
|
|
33
|
+
declare const presets: Readonly<Record<PresetName, PresetConfig>>;
|
|
34
|
+
/** Quick, responsive, minimal overshoot */
|
|
35
|
+
declare const snappy: Readonly<Required<SpringConfig>>;
|
|
36
|
+
/** Energetic, playful overshoot */
|
|
37
|
+
declare const bouncy: Readonly<Required<SpringConfig>>;
|
|
38
|
+
/** Gentle, elegant, no overshoot */
|
|
39
|
+
declare const smooth: Readonly<Required<SpringConfig>>;
|
|
40
|
+
/** Weighty, deliberate, slow settle */
|
|
41
|
+
declare const heavy: Readonly<Required<SpringConfig>>;
|
|
42
|
+
/**
|
|
43
|
+
* Get the current default preset configuration.
|
|
44
|
+
*/
|
|
45
|
+
declare function getDefaultPreset(): PresetConfig;
|
|
46
|
+
/**
|
|
47
|
+
* Get the current default preset name.
|
|
48
|
+
*/
|
|
49
|
+
declare function getDefaultPresetName(): PresetName;
|
|
50
|
+
/**
|
|
51
|
+
* Set the default preset for all animations.
|
|
52
|
+
* Call this at app initialization to change the global feel.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* // At app startup
|
|
57
|
+
* import { setDefaultPreset } from 'aerostat';
|
|
58
|
+
* setDefaultPreset('snappy');
|
|
59
|
+
*
|
|
60
|
+
* // All subsequent animations will use 'snappy' by default
|
|
61
|
+
* aerostat({ from: 0, to: 100, onUpdate: ... });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
declare function setDefaultPreset(preset: PresetName): void;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a preset name to its configuration.
|
|
67
|
+
* Returns undefined if the preset doesn't exist.
|
|
68
|
+
*/
|
|
69
|
+
declare function resolvePreset(name: PresetName): PresetConfig | undefined;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Spring configuration for physics-based animations.
|
|
73
|
+
* These values create a single "universal" spring feel.
|
|
74
|
+
*/
|
|
75
|
+
interface SpringConfig {
|
|
76
|
+
/** Stiffness of the spring (default: 180) */
|
|
77
|
+
stiffness?: number;
|
|
78
|
+
/** Damping of the spring (default: 12) */
|
|
79
|
+
damping?: number;
|
|
80
|
+
/** Mass of the animated object (default: 1) */
|
|
81
|
+
mass?: number;
|
|
82
|
+
/** Velocity threshold to consider animation complete (default: 0.001) */
|
|
83
|
+
restVelocity?: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Duration-based animation configuration.
|
|
87
|
+
*/
|
|
88
|
+
interface DurationConfig {
|
|
89
|
+
/** Duration in milliseconds (default: 300) */
|
|
90
|
+
duration?: number;
|
|
91
|
+
/** Easing function name (default: 'easeOut') */
|
|
92
|
+
easing?: EasingFunction;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Core animation configuration - the public API input.
|
|
96
|
+
*/
|
|
97
|
+
interface AnimationConfig {
|
|
98
|
+
/** Starting value */
|
|
99
|
+
from: number;
|
|
100
|
+
/** Target value */
|
|
101
|
+
to: number;
|
|
102
|
+
/** Called on each frame with interpolated value */
|
|
103
|
+
onUpdate: (value: number) => void;
|
|
104
|
+
/** Called when animation completes naturally */
|
|
105
|
+
onComplete?: () => void;
|
|
106
|
+
/** Initial velocity (for spring animations) */
|
|
107
|
+
velocity?: number;
|
|
108
|
+
/** Delay before animation starts, in milliseconds */
|
|
109
|
+
delay?: number;
|
|
110
|
+
/** Unique key for interrupt handling - new animations with same key will stop previous ones */
|
|
111
|
+
key?: string | symbol;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Spring-based animation configuration.
|
|
115
|
+
*/
|
|
116
|
+
interface SpringAnimationConfig extends AnimationConfig, SpringConfig {
|
|
117
|
+
/** Animation type discriminator */
|
|
118
|
+
type?: 'spring';
|
|
119
|
+
/** Use a preset instead of raw stiffness/damping values */
|
|
120
|
+
preset?: PresetName;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Duration-based animation configuration.
|
|
124
|
+
*/
|
|
125
|
+
interface DurationAnimationConfig extends AnimationConfig, DurationConfig {
|
|
126
|
+
/** Animation type discriminator */
|
|
127
|
+
type: 'duration';
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Combined animation config - accepts either spring or duration.
|
|
131
|
+
*/
|
|
132
|
+
type AerostatConfig = SpringAnimationConfig | DurationAnimationConfig;
|
|
133
|
+
/**
|
|
134
|
+
* Easing function - maps progress (0-1) to eased progress (0-1).
|
|
135
|
+
*/
|
|
136
|
+
type EasingFunction = (t: number) => number;
|
|
137
|
+
/**
|
|
138
|
+
* Named easing functions available in the library.
|
|
139
|
+
*/
|
|
140
|
+
type EasingName = 'linear' | 'easeOutExpo' | 'easeInOutCubic';
|
|
141
|
+
/**
|
|
142
|
+
* Internal job representation for the scheduler.
|
|
143
|
+
* Uses mutable state for zero-allocation hot path.
|
|
144
|
+
*/
|
|
145
|
+
interface AnimationJob {
|
|
146
|
+
/** Starting value (for duration-based interpolation) */
|
|
147
|
+
fromValue: number;
|
|
148
|
+
/** Current value */
|
|
149
|
+
value: number;
|
|
150
|
+
/** Target value */
|
|
151
|
+
to: number;
|
|
152
|
+
/** Current velocity (used by spring) */
|
|
153
|
+
velocity: number;
|
|
154
|
+
/** Update callback */
|
|
155
|
+
onUpdate: (value: number) => void;
|
|
156
|
+
/** Completion callback */
|
|
157
|
+
onComplete?: () => void;
|
|
158
|
+
/** Animation configuration */
|
|
159
|
+
config: ResolvedConfig;
|
|
160
|
+
/** Whether animation is active */
|
|
161
|
+
active: boolean;
|
|
162
|
+
/** Delay remaining (ms) */
|
|
163
|
+
delayRemaining: number;
|
|
164
|
+
/** Animation status */
|
|
165
|
+
status: AnimationStatus;
|
|
166
|
+
/** Start time (timestamp when animation began) */
|
|
167
|
+
startTime: number;
|
|
168
|
+
/** Elapsed time (accumulated, for pause/resume) */
|
|
169
|
+
elapsed: number;
|
|
170
|
+
/** Pause time (timestamp when paused, for calculating elapsed) */
|
|
171
|
+
pauseTime: number;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Internally resolved configuration with defaults applied.
|
|
175
|
+
*/
|
|
176
|
+
interface ResolvedConfig {
|
|
177
|
+
/** Animation type */
|
|
178
|
+
type: 'spring' | 'duration';
|
|
179
|
+
/** Spring stiffness (if spring type) */
|
|
180
|
+
stiffness: number;
|
|
181
|
+
/** Spring damping (if spring type) */
|
|
182
|
+
damping: number;
|
|
183
|
+
/** Spring mass (if spring type) */
|
|
184
|
+
mass: number;
|
|
185
|
+
/** Rest velocity threshold (if spring type) */
|
|
186
|
+
restVelocity: number;
|
|
187
|
+
/** Duration in ms (if duration type) */
|
|
188
|
+
duration: number;
|
|
189
|
+
/** Easing function (if duration type) */
|
|
190
|
+
easing: EasingFunction;
|
|
191
|
+
/** Start time (if duration type) */
|
|
192
|
+
startTime: number;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Animation status.
|
|
196
|
+
*/
|
|
197
|
+
type AnimationStatus = 'idle' | 'running' | 'paused' | 'completed';
|
|
198
|
+
/**
|
|
199
|
+
* Controller for an active animation.
|
|
200
|
+
* Provides full control over animation lifecycle.
|
|
201
|
+
*/
|
|
202
|
+
interface AnimationController {
|
|
203
|
+
/** Stop the animation immediately and remove from scheduler */
|
|
204
|
+
stop: () => void;
|
|
205
|
+
/** Update the target value mid-animation (for spring) */
|
|
206
|
+
setTarget: (to: number) => void;
|
|
207
|
+
/** Pause the animation (maintains current state) */
|
|
208
|
+
pause: () => void;
|
|
209
|
+
/** Resume a paused animation */
|
|
210
|
+
resume: () => void;
|
|
211
|
+
/** Get current animation status */
|
|
212
|
+
getStatus: () => AnimationStatus;
|
|
213
|
+
/** Get elapsed time in milliseconds */
|
|
214
|
+
getElapsed: () => number;
|
|
215
|
+
/** Get current value */
|
|
216
|
+
getValue: () => number;
|
|
217
|
+
/** Get current velocity */
|
|
218
|
+
getVelocity: () => number;
|
|
219
|
+
/** Whether animation is currently active */
|
|
220
|
+
readonly active: boolean;
|
|
221
|
+
/** Promise-like interface for chaining animations */
|
|
222
|
+
then: (callback: () => void) => AnimationController;
|
|
223
|
+
/** Get the underlying promise for async/await */
|
|
224
|
+
readonly finished: Promise<void>;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Options for the animate() function.
|
|
228
|
+
* Simplified API for object-based animations.
|
|
229
|
+
*/
|
|
230
|
+
interface TweenOptions {
|
|
231
|
+
/** Starting value */
|
|
232
|
+
from: number;
|
|
233
|
+
/** Target value - number or relative string like '+=100' or '-=50' */
|
|
234
|
+
to: number | string;
|
|
235
|
+
/** Duration in milliseconds (default: 300) */
|
|
236
|
+
duration?: number;
|
|
237
|
+
/** Delay before animation starts in milliseconds (default: 0) */
|
|
238
|
+
delay?: number;
|
|
239
|
+
/** Easing function (default: easeOutExpo) */
|
|
240
|
+
easing?: EasingFunction;
|
|
241
|
+
/** Called on each frame with interpolated value */
|
|
242
|
+
onUpdate: (value: number) => void;
|
|
243
|
+
/** Called when animation completes naturally */
|
|
244
|
+
onComplete?: () => void;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Internal task representation for WeakMap registry.
|
|
248
|
+
* Captures closure state for the animation.
|
|
249
|
+
*/
|
|
250
|
+
interface AnimationTask {
|
|
251
|
+
/** Start timestamp (set after delay) */
|
|
252
|
+
startTime: number;
|
|
253
|
+
/** Duration in ms */
|
|
254
|
+
duration: number;
|
|
255
|
+
/** Delay remaining in ms */
|
|
256
|
+
delayRemaining: number;
|
|
257
|
+
/** Starting value */
|
|
258
|
+
from: number;
|
|
259
|
+
/** Target value (resolved to number) */
|
|
260
|
+
to: number;
|
|
261
|
+
/** Easing function */
|
|
262
|
+
easing: EasingFunction;
|
|
263
|
+
/** Update callback */
|
|
264
|
+
onUpdate: (value: number) => void;
|
|
265
|
+
/** Completion callback */
|
|
266
|
+
onComplete?: () => void;
|
|
267
|
+
/** Whether task is active */
|
|
268
|
+
active: boolean;
|
|
269
|
+
/** Reference to the target object */
|
|
270
|
+
target: object;
|
|
271
|
+
/** Promise resolve function for .then() chaining */
|
|
272
|
+
resolve?: () => void;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Animate a target object with automatic interrupt.
|
|
277
|
+
*
|
|
278
|
+
* If the target already has an active animation,
|
|
279
|
+
* it is immediately stopped (Kill-on-Collision).
|
|
280
|
+
*
|
|
281
|
+
* @param target - The object to animate (used as WeakMap key)
|
|
282
|
+
* @param options - Animation options
|
|
283
|
+
* @returns Controller for the animation
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```ts
|
|
287
|
+
* const element = document.querySelector('.box');
|
|
288
|
+
*
|
|
289
|
+
* // Basic animation
|
|
290
|
+
* animate(element, {
|
|
291
|
+
* from: 0, to: 100,
|
|
292
|
+
* duration: 300,
|
|
293
|
+
* onUpdate: (v) => element.style.transform = `translateX(${v}px)`
|
|
294
|
+
* });
|
|
295
|
+
*
|
|
296
|
+
* // With delay (staggered menu items)
|
|
297
|
+
* animate(element, {
|
|
298
|
+
* from: 0, to: 1,
|
|
299
|
+
* duration: 200,
|
|
300
|
+
* delay: 100,
|
|
301
|
+
* onUpdate: (v) => element.style.opacity = String(v)
|
|
302
|
+
* });
|
|
303
|
+
*
|
|
304
|
+
* // Relative values
|
|
305
|
+
* animate(element, {
|
|
306
|
+
* from: 50, to: '+=100', // Animates to 150
|
|
307
|
+
* onUpdate: (v) => element.style.left = `${v}px`
|
|
308
|
+
* });
|
|
309
|
+
*
|
|
310
|
+
* // Chaining with .then()
|
|
311
|
+
* animate(element, { from: 0, to: 100, onUpdate: ... })
|
|
312
|
+
* .then(() => animate(element, { from: 100, to: 0, onUpdate: ... }));
|
|
313
|
+
*
|
|
314
|
+
* // Or with async/await
|
|
315
|
+
* await animate(element, { from: 0, to: 100, onUpdate: ... }).finished;
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
declare function animate(target: object, options: TweenOptions): AnimationController;
|
|
319
|
+
/**
|
|
320
|
+
* Check if a target has an active animation.
|
|
321
|
+
*/
|
|
322
|
+
declare function hasAnimation(target: object): boolean;
|
|
323
|
+
/**
|
|
324
|
+
* Stop animation on a specific target.
|
|
325
|
+
*/
|
|
326
|
+
declare function stopAnimation(target: object): void;
|
|
327
|
+
/**
|
|
328
|
+
* Get the number of active animations.
|
|
329
|
+
*/
|
|
330
|
+
declare function getActiveAnimationCount(): number;
|
|
331
|
+
|
|
332
|
+
export { type AnimationController as A, type DurationAnimationConfig as D, type EasingFunction as E, PRESET_BOUNCY as P, type ResolvedConfig as R, type SpringAnimationConfig as S, type TweenOptions as T, animate as a, type EasingName as b, type AerostatConfig as c, type AnimationJob as d, type AnimationConfig as e, type AnimationStatus as f, type AnimationTask as g, type DurationConfig as h, PRESET_HEAVY as i, PRESET_SMOOTH as j, PRESET_SNAPPY as k, type PresetConfig as l, type PresetName as m, type SpringConfig as n, bouncy as o, getActiveAnimationCount as p, getDefaultPreset as q, getDefaultPresetName as r, hasAnimation as s, heavy as t, presets as u, resolvePreset as v, setDefaultPreset as w, smooth as x, snappy as y, stopAnimation as z };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {i,d,a}from'./chunk-W7GGS6ZY.js';var C=[],O=false,E=null,h=0;function J(e){let s=h===0?0:Math.min((e-h)/1e3,.064);h=e;for(let r=C.length-1;r>=0;r--){let n=C[r];if(n.status==="paused")continue;if(n.delayRemaining>0){n.delayRemaining-=s*1e3;continue}if(n.startTime===0&&(n.startTime=e),!n.active){n.status="completed",C.splice(r,1);continue}n.elapsed=e-n.startTime;let u=false;n.config.type==="spring"?u=X(n,s):u=I(n),n.onUpdate(n.value),u||(n.active=false,n.status="completed",C.splice(r,1),n.onComplete?.());}C.length>0?E=requestAnimationFrame(J):(O=false,E=null,h=0);}function X(e,s){let{stiffness:r,damping:n,mass:u}=e.config;return d({get value(){return e.value},set value(i){e.value=i;},get velocity(){return e.velocity},set velocity(i){e.velocity=i;}},e.to,{stiffness:r,damping:n,mass:u},s)}function I(e){let{duration:s,easing:r}=e.config,n=e.elapsed,u=Math.min(n/s,1),a$1=r(u);e.value=a(e.fromValue,e.to,a$1);let i=r(Math.max(0,(n-16)/s));return e.velocity=(e.to-e.fromValue)*(a$1-i)/.016,n<s}function L(e){C.push(e),O||(O=true,h=0,E=requestAnimationFrame(J));}function U(e){let s=C.indexOf(e);s!==-1&&(C.splice(s,1),e.active=false,e.status="completed"),C.length===0&&E!==null&&(cancelAnimationFrame(E),E=null,O=false,h=0);}function M(e){e.status==="running"&&(e.status="paused",e.pauseTime=performance.now());}function N(e){if(e.status==="paused"){let s=performance.now()-e.pauseTime;e.startTime+=s,e.status="running";}}function Q(){return C.length}var w=Object.freeze({stiffness:400,damping:30,mass:1,restVelocity:.001}),$=Object.freeze({stiffness:180,damping:12,mass:1,restVelocity:.001}),H=Object.freeze({stiffness:120,damping:20,mass:1,restVelocity:.001}),q=Object.freeze({stiffness:100,damping:18,mass:2,restVelocity:.001}),z=Object.freeze({snappy:w,bouncy:$,smooth:H,heavy:q}),Z=w,j=$,ee=H,te=q,k="bouncy";function V(){return z[k]}function ne(){return k}function oe(e){k=e;}function F(e){return z[e]}var W={duration:300},b=new Map;function g(e){if(e.key!==void 0){let o=b.get(e.key);o&&o.stop();}let s=e.type??"spring",r=e.from,n=e.to,u=e.velocity??0,a=e.delay??0,i$1;if(s==="spring"){let o=e,d=(o.preset?F(o.preset):void 0)??V();i$1={type:"spring",stiffness:o.stiffness??d.stiffness,damping:o.damping??d.damping,mass:o.mass??d.mass,restVelocity:o.restVelocity??d.restVelocity,duration:0,easing:i,startTime:0};}else {let o=e;i$1={type:"duration",stiffness:0,damping:0,mass:0,restVelocity:0,duration:o.duration??W.duration,easing:o.easing??i,startTime:0};}let t={fromValue:r,value:r,to:n,velocity:u,onUpdate:e.onUpdate,...e.onComplete?{onComplete:e.onComplete}:{},config:i$1,active:true,delayRemaining:a,status:"running",startTime:0,elapsed:0,pauseTime:0};L(t);let p=true,f,l=new Promise(o=>{f=o;}),y={stop:()=>{p&&(p=false,U(t),e.key!==void 0&&b.delete(e.key),f());},setTarget:o=>{if(t.to=o,t.config.type==="duration"){let v=V();t.config.type="spring",t.config.stiffness=v.stiffness,t.config.damping=v.damping,t.config.mass=v.mass,t.config.restVelocity=v.restVelocity;}!t.active&&p&&(t.active=true,t.status="running",t.startTime=0,L(t));},pause:()=>{M(t);},resume:()=>{N(t);},getStatus:()=>t.status,getElapsed:()=>t.elapsed,getValue:()=>t.value,getVelocity:()=>t.velocity,get active(){return p&&t.active},then:o=>(l.then(o),y),get finished(){return l}};e.key!==void 0&&b.set(e.key,y);let m=t.onComplete;return t.onComplete=()=>{e.key!==void 0&&b.delete(e.key),p=false,m?.(),f();},y}function fe(e){let s=b.get(e);s&&s.stop();}function me(){for(let e of b.values())e.stop();}function de(){return b.size}var Y=e=>1-Math.pow(1-e,4);function Ce(e,s={}){let{pressScale:r=.92,pressDuration:n=80,releaseStiffness:u=500,releaseDamping:a=20}=s,i=1,t=null,p=false;function f(x){i=x,e.style.transform=`scale(${x})`;}function l(){p||(p=true,t?.stop(),t=g({from:i,to:r,type:"duration",duration:n,easing:Y,onUpdate:f}));}function y(){if(!p)return;p=false;let x=t?.getVelocity()??0;t?.stop(),t=g({from:i,to:1,type:"spring",stiffness:u,damping:a,velocity:x,onUpdate:f});}function m(x){e.setPointerCapture(x.pointerId),l();}function o(){y();}function v(){p&&y();}function d(){y();}function P(){l();}function A(){y();}function S(){l();}function c(){y();}return e.addEventListener("pointerdown",m),e.addEventListener("pointerup",o),e.addEventListener("pointerleave",v),e.addEventListener("pointercancel",d),e.addEventListener("mousedown",P),e.addEventListener("mouseup",A),e.addEventListener("touchstart",S,{passive:true}),e.addEventListener("touchend",c,{passive:true}),e.style.transformOrigin="center center",{destroy(){t?.stop(),e.removeEventListener("pointerdown",m),e.removeEventListener("pointerup",o),e.removeEventListener("pointerleave",v),e.removeEventListener("pointercancel",d),e.removeEventListener("mousedown",P),e.removeEventListener("mouseup",A),e.removeEventListener("touchstart",S),e.removeEventListener("touchend",c),e.style.transform="";}}}function he(e,s={}){let{amplitude:r=12,stiffness:n=1200,damping:u=20}=s,a=null,i=0;function t(){let f=a?.getVelocity()??0;a?.active?i:r;a?.stop();let y=a?.active?i+(i>=0?r:-r):r;a=g({from:y,to:0,velocity:f,stiffness:n,damping:u,mass:1,onUpdate:m=>{i=m,e.style.transform=`translateX(${m}px)`;},onComplete:()=>{e.style.transform="",i=0;}});}function p(){a?.stop(),e.style.transform="",i=0;}return {stop:p,shake:t,get active(){return a?.active??false}}}function Ee(e,s={}){let{amplitude:r=12,stiffness:n=1200,damping:u=20}=s;return g({from:r,to:0,stiffness:n,damping:u,mass:1,onUpdate:a=>{e.style.transform=`translateX(${a}px)`;},onComplete:()=>{e.style.transform="";}})}function Se(e,s={}){let {maxScale:r=1.05,peakOpacity:n=.6,stiffness:u=150,damping:a=25,color:i="#10b981",borderWidth:t=2}=s,p=null,f=null,l=null,m=1,o=0;function v(){if(l)return;let c=e.parentElement;if(!c)return;getComputedStyle(c).position==="static"&&(c.style.position="relative");let _=getComputedStyle(e).borderRadius||"0px";l=document.createElement("div"),l.style.cssText=`
|
|
2
|
+
position: absolute;
|
|
3
|
+
top: ${e.offsetTop-t}px;
|
|
4
|
+
left: ${e.offsetLeft-t}px;
|
|
5
|
+
width: ${e.offsetWidth+t*2}px;
|
|
6
|
+
height: ${e.offsetHeight+t*2}px;
|
|
7
|
+
border: ${t}px solid ${i};
|
|
8
|
+
border-radius: ${_};
|
|
9
|
+
pointer-events: none;
|
|
10
|
+
transform: scale(1);
|
|
11
|
+
opacity: 0;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
`,c.appendChild(l);}function d(){l&&(l.style.transform=`scale(${m})`,l.style.opacity=`${o}`);}function P(){v(),p?.stop(),f?.stop(),m=1,o=0,p=g({from:1,to:r,stiffness:u,damping:a,onUpdate:c=>{m=c,d();},onComplete:()=>{p=g({from:m,to:1,stiffness:u*.8,damping:a*1.2,onUpdate:c=>{m=c,d();}});}}),f=g({from:0,to:n,type:"duration",duration:100,onUpdate:c=>{o=c,d();},onComplete:()=>{f=g({from:n,to:0,type:"duration",duration:400,onUpdate:c=>{o=c,d();}});}});}function A(){p?.stop(),f?.stop(),m=1,o=0,d();}function S(){A(),l&&l.parentNode&&l.parentNode.removeChild(l),l=null;}return {pulse:P,stop:A,destroy:S,get active(){return (p?.active??false)||(f?.active??false)}}}export{L as a,U as b,M as c,N as d,Q as e,w as f,$ as g,H as h,q as i,z as j,Z as k,j as l,ee as m,te as n,V as o,ne as p,oe as q,F as r,g as s,fe as t,me as u,de as v,Ce as w,he as x,Ee as y,Se as z};//# sourceMappingURL=chunk-GZKQYUY7.js.map
|
|
14
|
+
//# sourceMappingURL=chunk-GZKQYUY7.js.map
|