@lagless/animate 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +472 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/animate.d.ts +14 -0
- package/dist/lib/animate.d.ts.map +1 -0
- package/dist/lib/animate.js +47 -0
- package/dist/lib/animate.js.map +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
# @lagless/animate
|
|
2
|
+
|
|
3
|
+
## 1. Responsibility & Context
|
|
4
|
+
|
|
5
|
+
Provides browser-based animation utilities using `requestAnimationFrame` for smooth, 60 FPS animations with customizable timing functions (easing). Includes `animate()` for callback-based animations, `animatePromise()` for async/await usage, and `AnimationCancelToken` for cancellation. This is a lightweight UI animation helper — not part of the deterministic ECS simulation — used for frontend effects like fades, transitions, and UI element animations.
|
|
6
|
+
|
|
7
|
+
## 2. Architecture Role
|
|
8
|
+
|
|
9
|
+
**UI layer** — browser-only utility library with no dependencies on other Lagless modules. Used by frontend UI code, not by ECS systems.
|
|
10
|
+
|
|
11
|
+
**Downstream consumers:**
|
|
12
|
+
- `circle-sumo-game` — Uses animations for UI transitions and visual effects
|
|
13
|
+
- Frontend React components — Any UI element that needs smooth animations
|
|
14
|
+
|
|
15
|
+
**Upstream dependencies:**
|
|
16
|
+
- None (uses browser APIs: `requestAnimationFrame`, `performance.now`)
|
|
17
|
+
|
|
18
|
+
## 3. Public API
|
|
19
|
+
|
|
20
|
+
### animate()
|
|
21
|
+
|
|
22
|
+
Starts a `requestAnimationFrame`-based animation. Calls `draw` function on every frame with progress value [0, 1].
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
function animate(
|
|
26
|
+
draw: (progress: number) => void, // Called every frame with progress [0, 1]
|
|
27
|
+
duration: number, // Animation duration in milliseconds
|
|
28
|
+
onAnimationDone: () => void, // Called when animation completes
|
|
29
|
+
timing?: TimingFunction // Timing function (default: easing)
|
|
30
|
+
): AnimationCancelToken; // Token for cancellation
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Parameters:**
|
|
34
|
+
- `draw` — Callback invoked on each frame with `progress` in [0, 1] (after applying timing function)
|
|
35
|
+
- `duration` — Total animation duration in milliseconds
|
|
36
|
+
- `onAnimationDone` — Callback invoked when animation completes (timeFraction reaches 1)
|
|
37
|
+
- `timing` — Timing function that transforms linear time fraction [0, 1] to eased progress [0, 1] (default: `easing`)
|
|
38
|
+
|
|
39
|
+
**Returns:** `AnimationCancelToken` — Call `cancel()` to stop animation
|
|
40
|
+
|
|
41
|
+
### animatePromise()
|
|
42
|
+
|
|
43
|
+
Promise-based wrapper for `animate()`. Resolves when animation completes.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
function animatePromise(
|
|
47
|
+
draw: (progress: number) => void,
|
|
48
|
+
duration: number,
|
|
49
|
+
timing?: TimingFunction
|
|
50
|
+
): Promise<void>;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Usage with async/await:**
|
|
54
|
+
```typescript
|
|
55
|
+
await animatePromise((progress) => {
|
|
56
|
+
element.style.opacity = progress.toString();
|
|
57
|
+
}, 500);
|
|
58
|
+
console.log('Animation complete!');
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### AnimationCancelToken
|
|
62
|
+
|
|
63
|
+
Cancellation token returned by `animate()`. Call `cancel()` to stop the animation.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
class AnimationCancelToken {
|
|
67
|
+
get isCancelled(): boolean; // True if cancel() was called
|
|
68
|
+
cancel(): void; // Stop the animation
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Behavior:** After `cancel()` is called, `draw` and `onAnimationDone` will not be invoked on subsequent frames. Animation loop exits gracefully.
|
|
73
|
+
|
|
74
|
+
### Timing Functions
|
|
75
|
+
|
|
76
|
+
Pre-defined timing functions that transform linear time to eased progress.
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
type TimingFunction = (timeFraction: number) => number;
|
|
80
|
+
|
|
81
|
+
// Pre-defined timing functions:
|
|
82
|
+
const easing: TimingFunction; // Ease-out (fast start, slow end) — default
|
|
83
|
+
const easingInOut: TimingFunction; // Ease-in-out (slow start, fast middle, slow end)
|
|
84
|
+
const linear: TimingFunction; // Linear (no easing)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Custom timing functions:**
|
|
88
|
+
```typescript
|
|
89
|
+
const customEasing: TimingFunction = (t) => t * t; // Quadratic ease-in
|
|
90
|
+
animate(draw, 1000, onDone, customEasing);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 4. Preconditions
|
|
94
|
+
|
|
95
|
+
- **Browser environment required** — This library uses `requestAnimationFrame` and `performance.now`, which are not available in Node.js
|
|
96
|
+
- **Duration must be positive** — Passing `duration <= 0` causes immediate completion (draws once with progress=1)
|
|
97
|
+
|
|
98
|
+
## 5. Postconditions
|
|
99
|
+
|
|
100
|
+
- After `animate()` completes (timeFraction reaches 1), `onAnimationDone()` is called
|
|
101
|
+
- After `animatePromise()` resolves, the animation has completed
|
|
102
|
+
- After `cancelToken.cancel()` is called, no further `draw` or `onAnimationDone` callbacks occur
|
|
103
|
+
|
|
104
|
+
## 6. Invariants & Constraints
|
|
105
|
+
|
|
106
|
+
- **Progress range:** `draw` is always called with `progress` in [0, 1] (clamped at 1 on final frame)
|
|
107
|
+
- **Frame rate:** Tied to browser's refresh rate (typically 60 FPS, but can be 120 FPS on high-refresh displays)
|
|
108
|
+
- **Cancellation is immediate:** After `cancel()`, no further callbacks occur (checked at start of each frame)
|
|
109
|
+
- **Timing function output:** Timing functions should map [0, 1] → [0, 1], but this is not enforced
|
|
110
|
+
|
|
111
|
+
## 7. Safety Notes (AI Agent)
|
|
112
|
+
|
|
113
|
+
### DO NOT
|
|
114
|
+
|
|
115
|
+
- **DO NOT use `animate()` inside ECS systems** — This is for UI animations only. ECS systems must be deterministic and cannot depend on `requestAnimationFrame`.
|
|
116
|
+
- **DO NOT assume 60 FPS** — Animation duration is in milliseconds, but actual frame rate depends on the browser. Use `progress` parameter, not frame count.
|
|
117
|
+
- **DO NOT forget to cancel animations on unmount** — React components should cancel animations in cleanup functions to prevent memory leaks.
|
|
118
|
+
- **DO NOT use in Node.js** — This library requires browser APIs (`requestAnimationFrame`, `performance.now`).
|
|
119
|
+
|
|
120
|
+
### Common Mistakes
|
|
121
|
+
|
|
122
|
+
- Using animations inside ECS systems → breaks determinism (use `interpolationFactor` from ECSSimulation instead)
|
|
123
|
+
- Not cancelling animations on component unmount → memory leak (callbacks continue after component is gone)
|
|
124
|
+
- Assuming fixed frame rate → animation speed varies on different displays (use `progress`, not frame count)
|
|
125
|
+
|
|
126
|
+
## 8. Usage Examples
|
|
127
|
+
|
|
128
|
+
### Basic Fade-In Animation
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { animate, easing } from '@lagless/animate';
|
|
132
|
+
|
|
133
|
+
const element = document.getElementById('myElement');
|
|
134
|
+
|
|
135
|
+
animate(
|
|
136
|
+
(progress) => {
|
|
137
|
+
element.style.opacity = progress.toString();
|
|
138
|
+
},
|
|
139
|
+
500, // 500ms duration
|
|
140
|
+
() => {
|
|
141
|
+
console.log('Fade-in complete!');
|
|
142
|
+
},
|
|
143
|
+
easing // ease-out timing
|
|
144
|
+
);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Promise-Based Animation with Async/Await
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { animatePromise, easingInOut } from '@lagless/animate';
|
|
151
|
+
|
|
152
|
+
async function fadeInThenOut(element: HTMLElement) {
|
|
153
|
+
// Fade in
|
|
154
|
+
await animatePromise((progress) => {
|
|
155
|
+
element.style.opacity = progress.toString();
|
|
156
|
+
}, 500, easingInOut);
|
|
157
|
+
|
|
158
|
+
// Wait 1 second
|
|
159
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
160
|
+
|
|
161
|
+
// Fade out
|
|
162
|
+
await animatePromise((progress) => {
|
|
163
|
+
element.style.opacity = (1 - progress).toString();
|
|
164
|
+
}, 500, easingInOut);
|
|
165
|
+
|
|
166
|
+
console.log('Fade sequence complete!');
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Cancellable Animation
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { animate, AnimationCancelToken } from '@lagless/animate';
|
|
174
|
+
|
|
175
|
+
let cancelToken: AnimationCancelToken | null = null;
|
|
176
|
+
|
|
177
|
+
function startAnimation() {
|
|
178
|
+
cancelToken = animate(
|
|
179
|
+
(progress) => {
|
|
180
|
+
element.style.transform = `translateX(${progress * 100}px)`;
|
|
181
|
+
},
|
|
182
|
+
1000,
|
|
183
|
+
() => console.log('Animation finished')
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function stopAnimation() {
|
|
188
|
+
if (cancelToken) {
|
|
189
|
+
cancelToken.cancel();
|
|
190
|
+
console.log('Animation cancelled');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Start animation
|
|
195
|
+
startAnimation();
|
|
196
|
+
|
|
197
|
+
// Cancel after 300ms
|
|
198
|
+
setTimeout(stopAnimation, 300);
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### React Component Integration
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import { useEffect, useState } from 'react';
|
|
205
|
+
import { animate, AnimationCancelToken } from '@lagless/animate';
|
|
206
|
+
|
|
207
|
+
function FadeInComponent({ children }) {
|
|
208
|
+
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
209
|
+
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
if (!ref) return;
|
|
212
|
+
|
|
213
|
+
const cancelToken = animate(
|
|
214
|
+
(progress) => {
|
|
215
|
+
ref.style.opacity = progress.toString();
|
|
216
|
+
},
|
|
217
|
+
500,
|
|
218
|
+
() => console.log('Component faded in')
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
// Cleanup: cancel animation on unmount
|
|
222
|
+
return () => {
|
|
223
|
+
cancelToken.cancel();
|
|
224
|
+
};
|
|
225
|
+
}, [ref]);
|
|
226
|
+
|
|
227
|
+
return <div ref={setRef} style={{ opacity: 0 }}>{children}</div>;
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Custom Timing Function
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { animate } from '@lagless/animate';
|
|
235
|
+
|
|
236
|
+
// Bounce effect (overshoots target)
|
|
237
|
+
const bounce: TimingFunction = (t) => {
|
|
238
|
+
const c4 = (2 * Math.PI) / 3;
|
|
239
|
+
return t === 0
|
|
240
|
+
? 0
|
|
241
|
+
: t === 1
|
|
242
|
+
? 1
|
|
243
|
+
: Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
animate(
|
|
247
|
+
(progress) => {
|
|
248
|
+
element.style.transform = `scale(${progress})`;
|
|
249
|
+
},
|
|
250
|
+
800,
|
|
251
|
+
() => console.log('Bounce animation complete'),
|
|
252
|
+
bounce
|
|
253
|
+
);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## 9. Testing Guidance
|
|
257
|
+
|
|
258
|
+
No tests currently exist for this library. When adding tests, consider:
|
|
259
|
+
|
|
260
|
+
**Framework suggestion:** Vitest with `jsdom` for browser API mocking
|
|
261
|
+
|
|
262
|
+
**Test coverage priorities:**
|
|
263
|
+
1. **Animation timing** — Verify `draw` is called with increasing progress values
|
|
264
|
+
2. **Cancellation** — Verify `draw` and `onAnimationDone` are not called after `cancel()`
|
|
265
|
+
3. **Duration** — Verify animation completes after specified duration (use fake timers)
|
|
266
|
+
4. **Timing functions** — Verify easing transforms input correctly (unit test timing functions independently)
|
|
267
|
+
|
|
268
|
+
**Example test pattern:**
|
|
269
|
+
```typescript
|
|
270
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
271
|
+
import { animate, easing } from '@lagless/animate';
|
|
272
|
+
|
|
273
|
+
describe('animate', () => {
|
|
274
|
+
it('should call draw with increasing progress values', async () => {
|
|
275
|
+
const draw = vi.fn();
|
|
276
|
+
const onDone = vi.fn();
|
|
277
|
+
|
|
278
|
+
animate(draw, 100, onDone);
|
|
279
|
+
|
|
280
|
+
// Wait for animation to complete
|
|
281
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
282
|
+
|
|
283
|
+
expect(draw).toHaveBeenCalled();
|
|
284
|
+
expect(draw.mock.calls.length).toBeGreaterThan(1);
|
|
285
|
+
|
|
286
|
+
// Verify progress is increasing
|
|
287
|
+
const progressValues = draw.mock.calls.map(call => call[0]);
|
|
288
|
+
for (let i = 1; i < progressValues.length; i++) {
|
|
289
|
+
expect(progressValues[i]).toBeGreaterThanOrEqual(progressValues[i - 1]);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
expect(onDone).toHaveBeenCalledTimes(1);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should cancel animation when token.cancel() is called', async () => {
|
|
296
|
+
const draw = vi.fn();
|
|
297
|
+
const onDone = vi.fn();
|
|
298
|
+
|
|
299
|
+
const cancelToken = animate(draw, 1000, onDone);
|
|
300
|
+
cancelToken.cancel();
|
|
301
|
+
|
|
302
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
303
|
+
|
|
304
|
+
// Draw may have been called once before cancellation, but not after
|
|
305
|
+
expect(onDone).not.toHaveBeenCalled();
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## 10. Change Checklist
|
|
311
|
+
|
|
312
|
+
When modifying this module:
|
|
313
|
+
|
|
314
|
+
1. **Preserve browser compatibility** — Ensure `requestAnimationFrame` and `performance.now` remain the only browser APIs used
|
|
315
|
+
2. **Test on high-refresh displays** — Verify animations work correctly at 120 FPS / 144 FPS
|
|
316
|
+
3. **Update this README:** Document new timing functions or API changes
|
|
317
|
+
4. **Maintain cancellation safety** — Ensure `cancel()` prevents all future callbacks
|
|
318
|
+
5. **Add tests:** Cover new functionality with unit tests
|
|
319
|
+
|
|
320
|
+
## 11. Integration Notes
|
|
321
|
+
|
|
322
|
+
### Used By
|
|
323
|
+
|
|
324
|
+
- **`circle-sumo-game`:**
|
|
325
|
+
- UI transitions (screen fades, button animations)
|
|
326
|
+
- Visual effects (particle animations, score popups)
|
|
327
|
+
|
|
328
|
+
- **React components:**
|
|
329
|
+
- Fade-in effects on mount
|
|
330
|
+
- Smooth transitions between states
|
|
331
|
+
|
|
332
|
+
### Common Integration Patterns
|
|
333
|
+
|
|
334
|
+
**React Hook for Fade-In:**
|
|
335
|
+
```typescript
|
|
336
|
+
import { useEffect, useRef } from 'react';
|
|
337
|
+
import { animate } from '@lagless/animate';
|
|
338
|
+
|
|
339
|
+
function useFadeIn(duration = 500) {
|
|
340
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
341
|
+
|
|
342
|
+
useEffect(() => {
|
|
343
|
+
if (!ref.current) return;
|
|
344
|
+
|
|
345
|
+
const element = ref.current;
|
|
346
|
+
element.style.opacity = '0';
|
|
347
|
+
|
|
348
|
+
const cancelToken = animate(
|
|
349
|
+
(progress) => {
|
|
350
|
+
element.style.opacity = progress.toString();
|
|
351
|
+
},
|
|
352
|
+
duration,
|
|
353
|
+
() => console.log('Fade-in complete')
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
return () => cancelToken.cancel();
|
|
357
|
+
}, [duration]);
|
|
358
|
+
|
|
359
|
+
return ref;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Usage
|
|
363
|
+
function MyComponent() {
|
|
364
|
+
const fadeInRef = useFadeIn(500);
|
|
365
|
+
return <div ref={fadeInRef}>Content</div>;
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**Chaining Animations:**
|
|
370
|
+
```typescript
|
|
371
|
+
import { animatePromise, easingInOut } from '@lagless/animate';
|
|
372
|
+
|
|
373
|
+
async function complexAnimation(element: HTMLElement) {
|
|
374
|
+
// Move right
|
|
375
|
+
await animatePromise((progress) => {
|
|
376
|
+
element.style.transform = `translateX(${progress * 200}px)`;
|
|
377
|
+
}, 500, easingInOut);
|
|
378
|
+
|
|
379
|
+
// Fade out
|
|
380
|
+
await animatePromise((progress) => {
|
|
381
|
+
element.style.opacity = (1 - progress).toString();
|
|
382
|
+
}, 300);
|
|
383
|
+
|
|
384
|
+
// Hide element
|
|
385
|
+
element.style.display = 'none';
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## 12. Appendix
|
|
390
|
+
|
|
391
|
+
### Timing Function Examples
|
|
392
|
+
|
|
393
|
+
**Visual representation (progress over time for 1000ms animation):**
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
Linear:
|
|
397
|
+
Progress
|
|
398
|
+
1.0 | ████
|
|
399
|
+
| ████████
|
|
400
|
+
| ████████
|
|
401
|
+
| ████████
|
|
402
|
+
| ████████
|
|
403
|
+
0.0 |████████
|
|
404
|
+
└────────────────────────────────────────────> Time (ms)
|
|
405
|
+
0 250 500 750 1000
|
|
406
|
+
|
|
407
|
+
Easing (ease-out):
|
|
408
|
+
Progress
|
|
409
|
+
1.0 | ████████████████████
|
|
410
|
+
| ████████
|
|
411
|
+
| ████
|
|
412
|
+
| ████
|
|
413
|
+
| ████
|
|
414
|
+
0.0 |████
|
|
415
|
+
└────────────────────────────────────────────> Time (ms)
|
|
416
|
+
0 250 500 750 1000
|
|
417
|
+
|
|
418
|
+
EasingInOut (ease-in-out):
|
|
419
|
+
Progress
|
|
420
|
+
1.0 | ████████████
|
|
421
|
+
| ████████
|
|
422
|
+
| ████
|
|
423
|
+
| ████
|
|
424
|
+
| ████
|
|
425
|
+
0.0 |████████
|
|
426
|
+
└────────────────────────────────────────────> Time (ms)
|
|
427
|
+
0 250 500 750 1000
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Timing Function Formulas
|
|
431
|
+
|
|
432
|
+
**Easing (ease-out):**
|
|
433
|
+
```typescript
|
|
434
|
+
const easing = (t) => 1 - Math.sin(Math.acos(t));
|
|
435
|
+
```
|
|
436
|
+
Fast start, slow end. Commonly used for fade-ins, slide-ins.
|
|
437
|
+
|
|
438
|
+
**EasingInOut:**
|
|
439
|
+
```typescript
|
|
440
|
+
const easingInOut = makeEaseInOut(easing);
|
|
441
|
+
|
|
442
|
+
// Internally transforms:
|
|
443
|
+
if (t < 0.5) return easing(2 * t) / 2; // First half: ease-in
|
|
444
|
+
else return (2 - easing(2 * (1 - t))) / 2; // Second half: ease-out
|
|
445
|
+
```
|
|
446
|
+
Slow start, fast middle, slow end. Commonly used for modal transitions.
|
|
447
|
+
|
|
448
|
+
**Linear:**
|
|
449
|
+
```typescript
|
|
450
|
+
const linear = (t) => t;
|
|
451
|
+
```
|
|
452
|
+
No easing. Constant speed throughout animation.
|
|
453
|
+
|
|
454
|
+
### Cancellation Behavior
|
|
455
|
+
|
|
456
|
+
**When `cancel()` is called:**
|
|
457
|
+
|
|
458
|
+
1. `cancelToken.isCancelled` is set to `true`
|
|
459
|
+
2. On the next `requestAnimationFrame` callback, the check `if (cancelToken.isCancelled) return;` exits the animation loop
|
|
460
|
+
3. No further `draw` or `onAnimationDone` callbacks occur
|
|
461
|
+
|
|
462
|
+
**Edge case:** If `cancel()` is called while the `draw` callback is executing, that frame completes normally, but the next frame is skipped.
|
|
463
|
+
|
|
464
|
+
**Example timeline:**
|
|
465
|
+
```
|
|
466
|
+
Frame 0: draw(0.0) [Animation starts]
|
|
467
|
+
Frame 1: draw(0.2)
|
|
468
|
+
Frame 2: draw(0.4) [User calls cancel() during this frame]
|
|
469
|
+
Frame 3: [Skipped due to cancellation check]
|
|
470
|
+
draw(0.6) [NOT called]
|
|
471
|
+
onAnimationDone() [NOT called]
|
|
472
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './lib/animate.js';\n"],"names":[],"rangeMappings":"","mappings":"AAAA,cAAc,mBAAmB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const easing: TimingFunction;
|
|
2
|
+
export declare const easingInOut: TimingFunction;
|
|
3
|
+
export declare const linear: TimingFunction;
|
|
4
|
+
export declare class AnimationCancelToken {
|
|
5
|
+
private _isCancelled;
|
|
6
|
+
get isCancelled(): boolean;
|
|
7
|
+
cancel(): void;
|
|
8
|
+
}
|
|
9
|
+
export declare const animatePromise: (draw: DrawFunction, duration: number, timing?: TimingFunction) => Promise<void>;
|
|
10
|
+
export declare function animate(draw: DrawFunction, duration: number, onAnimationDone: () => void, timing?: TimingFunction): AnimationCancelToken;
|
|
11
|
+
export type TimingFunction = (timeFraction: number) => number;
|
|
12
|
+
type DrawFunction = (progress: number) => void;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=animate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animate.d.ts","sourceRoot":"","sources":["../../src/lib/animate.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM,EAAE,cAAgF,CAAC;AACtG,eAAO,MAAM,WAAW,EAAE,cAAsC,CAAC;AACjE,eAAO,MAAM,MAAM,EAAE,cAAuD,CAAC;AAE7E,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,YAAY,CAAS;IAE7B,IAAW,WAAW,IAAI,OAAO,CAEhC;IAEM,MAAM,IAAI,IAAI;CAGtB;AAED,eAAO,MAAM,cAAc,GAAI,MAAM,YAAY,EAAE,UAAU,MAAM,EAAE,SAAS,cAAc,KAAG,OAAO,CAAC,IAAI,CAE1G,CAAC;AAEF,wBAAgB,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,IAAI,EAAE,MAAM,GAAE,cAAuB,GAAG,oBAAoB,CA0BhJ;AAWD,MAAM,MAAM,cAAc,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;AAC9D,KAAK,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const easing = (timeFraction)=>1 - Math.sin(Math.acos(timeFraction));
|
|
2
|
+
export const easingInOut = makeEaseInOut(easing);
|
|
3
|
+
export const linear = (timeFraction)=>timeFraction;
|
|
4
|
+
export class AnimationCancelToken {
|
|
5
|
+
get isCancelled() {
|
|
6
|
+
return this._isCancelled;
|
|
7
|
+
}
|
|
8
|
+
cancel() {
|
|
9
|
+
this._isCancelled = true;
|
|
10
|
+
}
|
|
11
|
+
constructor(){
|
|
12
|
+
this._isCancelled = false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export const animatePromise = (draw, duration, timing)=>{
|
|
16
|
+
return new Promise((resolve)=>animate(draw, duration, resolve, timing));
|
|
17
|
+
};
|
|
18
|
+
export function animate(draw, duration, onAnimationDone, timing = easing) {
|
|
19
|
+
const start = performance.now();
|
|
20
|
+
const cancelToken = new AnimationCancelToken();
|
|
21
|
+
if (cancelToken.isCancelled) {
|
|
22
|
+
return cancelToken;
|
|
23
|
+
}
|
|
24
|
+
requestAnimationFrame(function animate(time) {
|
|
25
|
+
if (cancelToken.isCancelled) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
let timeFraction = (time - start) / duration;
|
|
29
|
+
if (timeFraction > 1) timeFraction = 1;
|
|
30
|
+
const progress = timing(timeFraction);
|
|
31
|
+
draw(progress);
|
|
32
|
+
if (timeFraction < 1) {
|
|
33
|
+
requestAnimationFrame(animate);
|
|
34
|
+
} else if (onAnimationDone) {
|
|
35
|
+
onAnimationDone();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return cancelToken;
|
|
39
|
+
}
|
|
40
|
+
function makeEaseInOut(timing) {
|
|
41
|
+
return function(timeFraction) {
|
|
42
|
+
if (timeFraction < .5) return timing(2 * timeFraction) / 2;
|
|
43
|
+
else return (2 - timing(2 * (1 - timeFraction))) / 2;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=animate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/animate.ts"],"sourcesContent":["export const easing: TimingFunction = (timeFraction: number) => 1 - Math.sin(Math.acos(timeFraction));\nexport const easingInOut: TimingFunction = makeEaseInOut(easing);\nexport const linear: TimingFunction = (timeFraction: number) => timeFraction;\n\nexport class AnimationCancelToken {\n private _isCancelled = false;\n\n public get isCancelled(): boolean {\n return this._isCancelled;\n }\n\n public cancel(): void {\n this._isCancelled = true;\n }\n}\n\nexport const animatePromise = (draw: DrawFunction, duration: number, timing?: TimingFunction): Promise<void> => {\n return new Promise<void>((resolve) => animate(draw, duration, resolve, timing));\n};\n\nexport function animate(draw: DrawFunction, duration: number, onAnimationDone: () => void, timing: TimingFunction = easing): AnimationCancelToken {\n const start = performance.now();\n const cancelToken = new AnimationCancelToken();\n\n if (cancelToken.isCancelled) {\n return cancelToken;\n }\n\n requestAnimationFrame(function animate(time) {\n if (cancelToken.isCancelled) {\n return;\n }\n let timeFraction = (time - start) / duration;\n if (timeFraction > 1) timeFraction = 1;\n const progress = timing(timeFraction);\n\n draw(progress);\n\n if (timeFraction < 1) {\n requestAnimationFrame(animate);\n } else if (onAnimationDone) {\n onAnimationDone();\n }\n });\n\n return cancelToken;\n}\n\nfunction makeEaseInOut(timing: TimingFunction): TimingFunction {\n return function(timeFraction): number {\n if (timeFraction < .5)\n return timing(2 * timeFraction) / 2;\n else\n return (2 - timing(2 * (1 - timeFraction))) / 2;\n };\n}\n\nexport type TimingFunction = (timeFraction: number) => number;\ntype DrawFunction = (progress: number) => void;\n"],"names":["easing","timeFraction","Math","sin","acos","easingInOut","makeEaseInOut","linear","AnimationCancelToken","isCancelled","_isCancelled","cancel","animatePromise","draw","duration","timing","Promise","resolve","animate","onAnimationDone","start","performance","now","cancelToken","requestAnimationFrame","time","progress"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAAA,OAAO,MAAMA,SAAyB,CAACC,eAAyB,IAAIC,KAAKC,GAAG,CAACD,KAAKE,IAAI,CAACH,eAAe;AACtG,OAAO,MAAMI,cAA8BC,cAAcN,QAAQ;AACjE,OAAO,MAAMO,SAAyB,CAACN,eAAyBA,aAAa;AAE7E,OAAO,MAAMO;IAGX,IAAWC,cAAuB;QAChC,OAAO,IAAI,CAACC,YAAY;IAC1B;IAEOC,SAAe;QACpB,IAAI,CAACD,YAAY,GAAG;IACtB;;aARQA,eAAe;;AASzB;AAEA,OAAO,MAAME,iBAAiB,CAACC,MAAoBC,UAAkBC;IACnE,OAAO,IAAIC,QAAc,CAACC,UAAYC,QAAQL,MAAMC,UAAUG,SAASF;AACzE,EAAE;AAEF,OAAO,SAASG,QAAQL,IAAkB,EAAEC,QAAgB,EAAEK,eAA2B,EAAEJ,SAAyBf,MAAM;IACxH,MAAMoB,QAAQC,YAAYC,GAAG;IAC7B,MAAMC,cAAc,IAAIf;IAExB,IAAIe,YAAYd,WAAW,EAAE;QAC3B,OAAOc;IACT;IAEAC,sBAAsB,SAASN,QAAQO,IAAI;QACzC,IAAIF,YAAYd,WAAW,EAAE;YAC3B;QACF;QACA,IAAIR,eAAe,AAACwB,CAAAA,OAAOL,KAAI,IAAKN;QACpC,IAAIb,eAAe,GAAGA,eAAe;QACrC,MAAMyB,WAAWX,OAAOd;QAExBY,KAAKa;QAEL,IAAIzB,eAAe,GAAG;YACpBuB,sBAAsBN;QACxB,OAAO,IAAIC,iBAAiB;YAC1BA;QACF;IACF;IAEA,OAAOI;AACT;AAEA,SAASjB,cAAcS,MAAsB;IAC3C,OAAO,SAASd,YAAY;QAC1B,IAAIA,eAAe,IACjB,OAAOc,OAAO,IAAId,gBAAgB;aAElC,OAAO,AAAC,CAAA,IAAIc,OAAO,IAAK,CAAA,IAAId,YAAW,EAAE,IAAK;IAClD;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":"5.9.3"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lagless/animate",
|
|
3
|
+
"version": "0.0.33",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/ppauel/lagless",
|
|
8
|
+
"directory": "libs/animate"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
"./package.json": "./package.json",
|
|
16
|
+
".": {
|
|
17
|
+
"@lagless/source": "./src/index.ts",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"nx": {
|
|
24
|
+
"sourceRoot": "libs/animate/src",
|
|
25
|
+
"targets": {
|
|
26
|
+
"build": {
|
|
27
|
+
"executor": "@nx/js:swc",
|
|
28
|
+
"outputs": [
|
|
29
|
+
"{options.outputPath}"
|
|
30
|
+
],
|
|
31
|
+
"options": {
|
|
32
|
+
"outputPath": "libs/animate/dist",
|
|
33
|
+
"main": "libs/animate/src/index.ts",
|
|
34
|
+
"tsConfig": "libs/animate/tsconfig.lib.json",
|
|
35
|
+
"skipTypeCheck": true,
|
|
36
|
+
"stripLeadingPaths": true
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@swc/helpers": "~0.5.11"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"dist",
|
|
46
|
+
"README.md"
|
|
47
|
+
],
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
}
|
|
51
|
+
}
|