mintwaterfall 0.8.6
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/CHANGELOG.md +223 -0
- package/CONTRIBUTING.md +199 -0
- package/README.md +363 -0
- package/dist/index.d.ts +149 -0
- package/dist/mintwaterfall.cjs.js +7978 -0
- package/dist/mintwaterfall.esm.js +7907 -0
- package/dist/mintwaterfall.min.js +7 -0
- package/dist/mintwaterfall.umd.js +7978 -0
- package/index.d.ts +149 -0
- package/package.json +126 -0
- package/src/enterprise/enterprise-core.js +0 -0
- package/src/enterprise/enterprise-feature-template.js +0 -0
- package/src/enterprise/feature-registry.js +0 -0
- package/src/enterprise/features/breakdown.js +0 -0
- package/src/features/breakdown.js +0 -0
- package/src/features/conditional-formatting.js +0 -0
- package/src/index.js +111 -0
- package/src/mintwaterfall-accessibility.ts +680 -0
- package/src/mintwaterfall-advanced-data.ts +1034 -0
- package/src/mintwaterfall-advanced-interactions.ts +649 -0
- package/src/mintwaterfall-advanced-performance.ts +582 -0
- package/src/mintwaterfall-animations.ts +595 -0
- package/src/mintwaterfall-brush.ts +471 -0
- package/src/mintwaterfall-chart-core.ts +296 -0
- package/src/mintwaterfall-chart.ts +1915 -0
- package/src/mintwaterfall-data.ts +1100 -0
- package/src/mintwaterfall-export.ts +475 -0
- package/src/mintwaterfall-hierarchical-layouts.ts +724 -0
- package/src/mintwaterfall-layouts.ts +647 -0
- package/src/mintwaterfall-performance.ts +573 -0
- package/src/mintwaterfall-scales.ts +437 -0
- package/src/mintwaterfall-shapes.ts +385 -0
- package/src/mintwaterfall-statistics.ts +821 -0
- package/src/mintwaterfall-themes.ts +391 -0
- package/src/mintwaterfall-tooltip.ts +450 -0
- package/src/mintwaterfall-zoom.ts +399 -0
- package/src/types/js-modules.d.ts +25 -0
- package/src/utils/compatibility-layer.js +0 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
// MintWaterfall Animation and Transitions System - TypeScript Version
|
|
2
|
+
// Provides smooth animations and transitions for chart updates with full type safety
|
|
3
|
+
|
|
4
|
+
// Type definitions for animation system
|
|
5
|
+
export interface EasingFunction {
|
|
6
|
+
(t: number): number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface EasingFunctions {
|
|
10
|
+
linear: EasingFunction;
|
|
11
|
+
easeInQuad: EasingFunction;
|
|
12
|
+
easeOutQuad: EasingFunction;
|
|
13
|
+
easeInOutQuad: EasingFunction;
|
|
14
|
+
easeInCubic: EasingFunction;
|
|
15
|
+
easeOutCubic: EasingFunction;
|
|
16
|
+
easeInOutCubic: EasingFunction;
|
|
17
|
+
easeInSine: EasingFunction;
|
|
18
|
+
easeOutSine: EasingFunction;
|
|
19
|
+
easeInOutSine: EasingFunction;
|
|
20
|
+
easeInElastic: EasingFunction;
|
|
21
|
+
easeOutElastic: EasingFunction;
|
|
22
|
+
easeOutBounce: EasingFunction;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface TransitionConfig {
|
|
26
|
+
staggerDelay: number;
|
|
27
|
+
defaultDuration: number;
|
|
28
|
+
defaultEase: keyof EasingFunctions;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface AnimationOptions {
|
|
32
|
+
delay?: number;
|
|
33
|
+
duration?: number;
|
|
34
|
+
ease?: keyof EasingFunctions;
|
|
35
|
+
reverse?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface TransitionStep {
|
|
39
|
+
fn: () => Promise<any>;
|
|
40
|
+
delay: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface TransitionSequence {
|
|
44
|
+
add(transitionFn: () => Promise<any>, delay?: number): TransitionSequence;
|
|
45
|
+
parallel(...transitionFns: (() => Promise<any>)[]): TransitionSequence;
|
|
46
|
+
play(): Promise<void>;
|
|
47
|
+
stop(): void;
|
|
48
|
+
readonly isRunning: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface SpringAnimation {
|
|
52
|
+
animate(
|
|
53
|
+
startValue: number,
|
|
54
|
+
endValue: number,
|
|
55
|
+
onUpdate?: (value: number) => void,
|
|
56
|
+
onComplete?: () => void
|
|
57
|
+
): void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface TransitionWithEvents {
|
|
61
|
+
start(): TransitionWithEvents;
|
|
62
|
+
interrupt(): TransitionWithEvents;
|
|
63
|
+
then(callback?: () => void): TransitionWithEvents;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface TransitionEventConfig {
|
|
67
|
+
duration?: number;
|
|
68
|
+
onStart?: () => void;
|
|
69
|
+
onEnd?: () => void;
|
|
70
|
+
onInterrupt?: () => void;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface AnimationPresets {
|
|
74
|
+
slideInLeft(element: any, duration?: number): Promise<void>;
|
|
75
|
+
slideInRight(element: any, duration?: number): Promise<void>;
|
|
76
|
+
fadeIn(element: any, duration?: number): Promise<void>;
|
|
77
|
+
fadeOut(element: any, duration?: number): Promise<void>;
|
|
78
|
+
scaleIn(element: any, duration?: number): Promise<void>;
|
|
79
|
+
scaleOut(element: any, duration?: number): Promise<void>;
|
|
80
|
+
pulse(element: any, duration?: number): Promise<void>;
|
|
81
|
+
bounce(element: any, duration?: number): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface AnimationSystem {
|
|
85
|
+
easingFunctions: EasingFunctions;
|
|
86
|
+
animateValue(
|
|
87
|
+
startValue: number,
|
|
88
|
+
endValue: number,
|
|
89
|
+
duration: number,
|
|
90
|
+
easingType?: keyof EasingFunctions,
|
|
91
|
+
onUpdate?: (value: number, progress: number) => void,
|
|
92
|
+
onComplete?: () => void
|
|
93
|
+
): void;
|
|
94
|
+
staggeredAnimation(
|
|
95
|
+
items: any[],
|
|
96
|
+
animationFn: (item: any, index: number, duration: number) => void,
|
|
97
|
+
staggerDelay?: number,
|
|
98
|
+
totalDuration?: number
|
|
99
|
+
): void;
|
|
100
|
+
morphShape(
|
|
101
|
+
fromPath: string,
|
|
102
|
+
toPath: string,
|
|
103
|
+
duration?: number,
|
|
104
|
+
easingType?: keyof EasingFunctions,
|
|
105
|
+
onUpdate?: (path: string, progress: number) => void,
|
|
106
|
+
onComplete?: () => void
|
|
107
|
+
): void;
|
|
108
|
+
fadeTransition(
|
|
109
|
+
element: any,
|
|
110
|
+
fromOpacity: number,
|
|
111
|
+
toOpacity: number,
|
|
112
|
+
duration?: number,
|
|
113
|
+
easingType?: keyof EasingFunctions
|
|
114
|
+
): Promise<void>;
|
|
115
|
+
slideTransition(
|
|
116
|
+
element: any,
|
|
117
|
+
fromX: number,
|
|
118
|
+
toX: number,
|
|
119
|
+
duration?: number,
|
|
120
|
+
easingType?: keyof EasingFunctions
|
|
121
|
+
): Promise<void>;
|
|
122
|
+
scaleTransition(
|
|
123
|
+
element: any,
|
|
124
|
+
fromScale: number,
|
|
125
|
+
toScale: number,
|
|
126
|
+
duration?: number,
|
|
127
|
+
easingType?: keyof EasingFunctions
|
|
128
|
+
): Promise<void>;
|
|
129
|
+
createTransitionSequence(): TransitionSequence;
|
|
130
|
+
createSpringAnimation(tension?: number, friction?: number): SpringAnimation;
|
|
131
|
+
createStaggeredTransition(
|
|
132
|
+
elements: any[] | NodeListOf<Element>,
|
|
133
|
+
animationFn: (element: any, duration: number, ease: keyof EasingFunctions) => Promise<any>,
|
|
134
|
+
options?: AnimationOptions
|
|
135
|
+
): Promise<any[]>;
|
|
136
|
+
createCustomTween(startValue: number, endValue: number, interpolator?: (start: number, end: number, t: number) => number): (t: number) => number;
|
|
137
|
+
createTransitionWithEvents(element: any, config: TransitionEventConfig): TransitionWithEvents;
|
|
138
|
+
transitionConfig: TransitionConfig;
|
|
139
|
+
presets: AnimationPresets;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function createAnimationSystem(): AnimationSystem {
|
|
143
|
+
|
|
144
|
+
// Advanced transition configuration
|
|
145
|
+
const transitionConfig: TransitionConfig = {
|
|
146
|
+
staggerDelay: 100, // Default stagger delay between elements
|
|
147
|
+
defaultDuration: 750, // Default animation duration
|
|
148
|
+
defaultEase: "easeOutQuad"
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
function createEasingFunctions(): EasingFunctions {
|
|
152
|
+
return {
|
|
153
|
+
linear: (t: number) => t,
|
|
154
|
+
easeInQuad: (t: number) => t * t,
|
|
155
|
+
easeOutQuad: (t: number) => t * (2 - t),
|
|
156
|
+
easeInOutQuad: (t: number) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
|
|
157
|
+
easeInCubic: (t: number) => t * t * t,
|
|
158
|
+
easeOutCubic: (t: number) => (--t) * t * t + 1,
|
|
159
|
+
easeInOutCubic: (t: number) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
|
|
160
|
+
easeInSine: (t: number) => 1 - Math.cos(t * Math.PI / 2),
|
|
161
|
+
easeOutSine: (t: number) => Math.sin(t * Math.PI / 2),
|
|
162
|
+
easeInOutSine: (t: number) => -(Math.cos(Math.PI * t) - 1) / 2,
|
|
163
|
+
easeInElastic: (t: number) => {
|
|
164
|
+
const c4 = (2 * Math.PI) / 3;
|
|
165
|
+
return t === 0 ? 0 : t === 1 ? 1 : -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * c4);
|
|
166
|
+
},
|
|
167
|
+
easeOutElastic: (t: number) => {
|
|
168
|
+
const c4 = (2 * Math.PI) / 3;
|
|
169
|
+
return t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
|
|
170
|
+
},
|
|
171
|
+
easeOutBounce: (t: number) => {
|
|
172
|
+
const n1 = 7.5625;
|
|
173
|
+
const d1 = 2.75;
|
|
174
|
+
|
|
175
|
+
if (t < 1 / d1) {
|
|
176
|
+
return n1 * t * t;
|
|
177
|
+
} else if (t < 2 / d1) {
|
|
178
|
+
return n1 * (t -= 1.5 / d1) * t + 0.75;
|
|
179
|
+
} else if (t < 2.5 / d1) {
|
|
180
|
+
return n1 * (t -= 2.25 / d1) * t + 0.9375;
|
|
181
|
+
} else {
|
|
182
|
+
return n1 * (t -= 2.625 / d1) * t + 0.984375;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const easingFunctions = createEasingFunctions();
|
|
189
|
+
|
|
190
|
+
function animateValue(
|
|
191
|
+
startValue: number,
|
|
192
|
+
endValue: number,
|
|
193
|
+
duration: number,
|
|
194
|
+
easingType: keyof EasingFunctions = "easeOutQuad",
|
|
195
|
+
onUpdate?: (value: number, progress: number) => void,
|
|
196
|
+
onComplete?: () => void
|
|
197
|
+
): void {
|
|
198
|
+
const startTime = performance.now();
|
|
199
|
+
const valueRange = endValue - startValue;
|
|
200
|
+
const easing = easingFunctions[easingType] || easingFunctions.easeOutQuad;
|
|
201
|
+
|
|
202
|
+
function frame(currentTime: number): void {
|
|
203
|
+
const elapsed = currentTime - startTime;
|
|
204
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
205
|
+
|
|
206
|
+
const easedProgress = easing(progress);
|
|
207
|
+
const currentValue = startValue + (valueRange * easedProgress);
|
|
208
|
+
|
|
209
|
+
if (onUpdate) {
|
|
210
|
+
onUpdate(currentValue, progress);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (progress < 1) {
|
|
214
|
+
requestAnimationFrame(frame);
|
|
215
|
+
} else if (onComplete) {
|
|
216
|
+
onComplete();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
requestAnimationFrame(frame);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function staggeredAnimation(
|
|
224
|
+
items: any[],
|
|
225
|
+
animationFn: (item: any, index: number, duration: number) => void,
|
|
226
|
+
staggerDelay: number = 100,
|
|
227
|
+
totalDuration: number = 1000
|
|
228
|
+
): void {
|
|
229
|
+
if (!Array.isArray(items)) {
|
|
230
|
+
throw new Error("Items must be an array");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
items.forEach((item, index) => {
|
|
234
|
+
const delay = index * staggerDelay;
|
|
235
|
+
const adjustedDuration = totalDuration - delay;
|
|
236
|
+
|
|
237
|
+
setTimeout(() => {
|
|
238
|
+
if (adjustedDuration > 0) {
|
|
239
|
+
animationFn(item, index, adjustedDuration);
|
|
240
|
+
}
|
|
241
|
+
}, delay);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function morphShape(
|
|
246
|
+
fromPath: string,
|
|
247
|
+
toPath: string,
|
|
248
|
+
duration: number = 1000,
|
|
249
|
+
easingType: keyof EasingFunctions = "easeInOutQuad",
|
|
250
|
+
onUpdate?: (path: string, progress: number) => void,
|
|
251
|
+
onComplete?: () => void
|
|
252
|
+
): void {
|
|
253
|
+
// Simple path morphing for basic shapes
|
|
254
|
+
// Note: In a real implementation, you'd want more sophisticated path interpolation
|
|
255
|
+
|
|
256
|
+
if (typeof fromPath !== "string" || typeof toPath !== "string") {
|
|
257
|
+
throw new Error("Path values must be strings");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const startTime = performance.now();
|
|
261
|
+
const easing = easingFunctions[easingType] || easingFunctions.easeInOutQuad;
|
|
262
|
+
|
|
263
|
+
function frame(currentTime: number): void {
|
|
264
|
+
const elapsed = currentTime - startTime;
|
|
265
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
266
|
+
|
|
267
|
+
const easedProgress = easing(progress);
|
|
268
|
+
|
|
269
|
+
// Simple interpolation - in production, use a proper path morphing library
|
|
270
|
+
const interpolatedPath = progress < 0.5 ? fromPath : toPath;
|
|
271
|
+
|
|
272
|
+
if (onUpdate) {
|
|
273
|
+
onUpdate(interpolatedPath, easedProgress);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (progress < 1) {
|
|
277
|
+
requestAnimationFrame(frame);
|
|
278
|
+
} else if (onComplete) {
|
|
279
|
+
onComplete();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
requestAnimationFrame(frame);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function fadeTransition(
|
|
287
|
+
element: any,
|
|
288
|
+
fromOpacity: number,
|
|
289
|
+
toOpacity: number,
|
|
290
|
+
duration: number = 500,
|
|
291
|
+
easingType: keyof EasingFunctions = "easeOutQuad"
|
|
292
|
+
): Promise<void> {
|
|
293
|
+
return new Promise((resolve) => {
|
|
294
|
+
animateValue(
|
|
295
|
+
fromOpacity,
|
|
296
|
+
toOpacity,
|
|
297
|
+
duration,
|
|
298
|
+
easingType,
|
|
299
|
+
(value) => {
|
|
300
|
+
if (element && element.style) {
|
|
301
|
+
element.style.opacity = value.toString();
|
|
302
|
+
} else if (element && element.attr) {
|
|
303
|
+
// D3 selection
|
|
304
|
+
element.attr("opacity", value);
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
() => resolve()
|
|
308
|
+
);
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function slideTransition(
|
|
313
|
+
element: any,
|
|
314
|
+
fromX: number,
|
|
315
|
+
toX: number,
|
|
316
|
+
duration: number = 500,
|
|
317
|
+
easingType: keyof EasingFunctions = "easeOutQuad"
|
|
318
|
+
): Promise<void> {
|
|
319
|
+
return new Promise((resolve) => {
|
|
320
|
+
animateValue(
|
|
321
|
+
fromX,
|
|
322
|
+
toX,
|
|
323
|
+
duration,
|
|
324
|
+
easingType,
|
|
325
|
+
(value) => {
|
|
326
|
+
if (element && element.style) {
|
|
327
|
+
element.style.transform = `translateX(${value}px)`;
|
|
328
|
+
} else if (element && element.attr) {
|
|
329
|
+
// D3 selection
|
|
330
|
+
element.attr("transform", `translate(${value}, 0)`);
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
() => resolve()
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function scaleTransition(
|
|
339
|
+
element: any,
|
|
340
|
+
fromScale: number,
|
|
341
|
+
toScale: number,
|
|
342
|
+
duration: number = 500,
|
|
343
|
+
easingType: keyof EasingFunctions = "easeOutQuad"
|
|
344
|
+
): Promise<void> {
|
|
345
|
+
return new Promise((resolve) => {
|
|
346
|
+
animateValue(
|
|
347
|
+
fromScale,
|
|
348
|
+
toScale,
|
|
349
|
+
duration,
|
|
350
|
+
easingType,
|
|
351
|
+
(value) => {
|
|
352
|
+
if (element && element.style) {
|
|
353
|
+
element.style.transform = `scale(${value})`;
|
|
354
|
+
} else if (element && element.attr) {
|
|
355
|
+
// D3 selection
|
|
356
|
+
element.attr("transform", `scale(${value})`);
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
() => resolve()
|
|
360
|
+
);
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function createTransitionSequence(): TransitionSequence {
|
|
365
|
+
const sequence: TransitionStep[] = [];
|
|
366
|
+
let isRunning = false;
|
|
367
|
+
|
|
368
|
+
function add(transitionFn: () => Promise<any>, delay: number = 0): TransitionSequence {
|
|
369
|
+
sequence.push({ fn: transitionFn, delay });
|
|
370
|
+
return transitionSequence;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function parallel(...transitionFns: (() => Promise<any>)[]): TransitionSequence {
|
|
374
|
+
sequence.push({
|
|
375
|
+
fn: () => Promise.all(transitionFns.map(fn => fn())),
|
|
376
|
+
delay: 0
|
|
377
|
+
});
|
|
378
|
+
return transitionSequence;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async function play(): Promise<void> {
|
|
382
|
+
if (isRunning) {
|
|
383
|
+
throw new Error("Sequence is already running");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
isRunning = true;
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
for (const step of sequence) {
|
|
390
|
+
if (step.delay > 0) {
|
|
391
|
+
await new Promise<void>(resolve => setTimeout(resolve, step.delay));
|
|
392
|
+
}
|
|
393
|
+
await step.fn();
|
|
394
|
+
}
|
|
395
|
+
} finally {
|
|
396
|
+
isRunning = false;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function stop(): void {
|
|
401
|
+
isRunning = false;
|
|
402
|
+
// Note: In a production system, you'd want to cancel running animations
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const transitionSequence: TransitionSequence = {
|
|
406
|
+
add,
|
|
407
|
+
parallel,
|
|
408
|
+
play,
|
|
409
|
+
stop,
|
|
410
|
+
get isRunning() { return isRunning; }
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
return transitionSequence;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function createSpringAnimation(tension: number = 300, friction: number = 20): SpringAnimation {
|
|
417
|
+
// Simple spring physics implementation
|
|
418
|
+
function animate(
|
|
419
|
+
startValue: number,
|
|
420
|
+
endValue: number,
|
|
421
|
+
onUpdate?: (value: number) => void,
|
|
422
|
+
onComplete?: () => void
|
|
423
|
+
): void {
|
|
424
|
+
let position = startValue;
|
|
425
|
+
let velocity = 0;
|
|
426
|
+
let lastTime = performance.now();
|
|
427
|
+
|
|
428
|
+
function frame(currentTime: number): void {
|
|
429
|
+
const deltaTime = (currentTime - lastTime) / 1000; // Convert to seconds
|
|
430
|
+
lastTime = currentTime;
|
|
431
|
+
|
|
432
|
+
const displacement = position - endValue;
|
|
433
|
+
const springForce = -tension * displacement;
|
|
434
|
+
const dampingForce = -friction * velocity;
|
|
435
|
+
|
|
436
|
+
const acceleration = springForce + dampingForce;
|
|
437
|
+
velocity += acceleration * deltaTime;
|
|
438
|
+
position += velocity * deltaTime;
|
|
439
|
+
|
|
440
|
+
if (onUpdate) {
|
|
441
|
+
onUpdate(position);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Check if animation should continue
|
|
445
|
+
const isAtRest = Math.abs(displacement) < 0.01 && Math.abs(velocity) < 0.01;
|
|
446
|
+
|
|
447
|
+
if (!isAtRest) {
|
|
448
|
+
requestAnimationFrame(frame);
|
|
449
|
+
} else if (onComplete) {
|
|
450
|
+
onComplete();
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
requestAnimationFrame(frame);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return { animate };
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function createAnimationPresets(): AnimationPresets {
|
|
461
|
+
return {
|
|
462
|
+
slideInLeft: (element: any, duration: number = 500) =>
|
|
463
|
+
slideTransition(element, -100, 0, duration, "easeOutQuad"),
|
|
464
|
+
|
|
465
|
+
slideInRight: (element: any, duration: number = 500) =>
|
|
466
|
+
slideTransition(element, 100, 0, duration, "easeOutQuad"),
|
|
467
|
+
|
|
468
|
+
fadeIn: (element: any, duration: number = 500) =>
|
|
469
|
+
fadeTransition(element, 0, 1, duration, "easeOutQuad"),
|
|
470
|
+
|
|
471
|
+
fadeOut: (element: any, duration: number = 500) =>
|
|
472
|
+
fadeTransition(element, 1, 0, duration, "easeOutQuad"),
|
|
473
|
+
|
|
474
|
+
scaleIn: (element: any, duration: number = 500) =>
|
|
475
|
+
scaleTransition(element, 0, 1, duration, "easeOutElastic"),
|
|
476
|
+
|
|
477
|
+
scaleOut: (element: any, duration: number = 500) =>
|
|
478
|
+
scaleTransition(element, 1, 0, duration, "easeInQuad"),
|
|
479
|
+
|
|
480
|
+
pulse: (element: any, duration: number = 300) => {
|
|
481
|
+
const sequence = createTransitionSequence();
|
|
482
|
+
return sequence
|
|
483
|
+
.add(() => scaleTransition(element, 1, 1.1, duration / 2, "easeOutQuad"))
|
|
484
|
+
.add(() => scaleTransition(element, 1.1, 1, duration / 2, "easeInQuad"))
|
|
485
|
+
.play();
|
|
486
|
+
},
|
|
487
|
+
|
|
488
|
+
bounce: (element: any, duration: number = 600) =>
|
|
489
|
+
scaleTransition(element, 0, 1, duration, "easeOutBounce")
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const presets = createAnimationPresets();
|
|
494
|
+
|
|
495
|
+
// Advanced staggered animations
|
|
496
|
+
function createStaggeredTransition(
|
|
497
|
+
elements: any[] | NodeListOf<Element>,
|
|
498
|
+
animationFn: (element: any, duration: number, ease: keyof EasingFunctions) => Promise<any>,
|
|
499
|
+
options: AnimationOptions = {}
|
|
500
|
+
): Promise<any[]> {
|
|
501
|
+
const {
|
|
502
|
+
delay = transitionConfig.staggerDelay,
|
|
503
|
+
duration = transitionConfig.defaultDuration,
|
|
504
|
+
ease = transitionConfig.defaultEase,
|
|
505
|
+
reverse = false
|
|
506
|
+
} = options;
|
|
507
|
+
|
|
508
|
+
const elementArray = Array.isArray(elements) ? elements : Array.from(elements);
|
|
509
|
+
const orderedElements = reverse ? [...elementArray].reverse() : elementArray;
|
|
510
|
+
|
|
511
|
+
return Promise.all(
|
|
512
|
+
orderedElements.map((element, index) => {
|
|
513
|
+
return new Promise<any>(resolve => {
|
|
514
|
+
setTimeout(() => {
|
|
515
|
+
animationFn(element, duration, ease).then(resolve);
|
|
516
|
+
}, index * delay);
|
|
517
|
+
});
|
|
518
|
+
})
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Custom tweening functions
|
|
523
|
+
function createCustomTween(
|
|
524
|
+
startValue: number,
|
|
525
|
+
endValue: number,
|
|
526
|
+
interpolator?: (start: number, end: number, t: number) => number
|
|
527
|
+
): (t: number) => number {
|
|
528
|
+
return function(t: number): number {
|
|
529
|
+
if (typeof interpolator === "function") {
|
|
530
|
+
return interpolator(startValue, endValue, t);
|
|
531
|
+
}
|
|
532
|
+
// Default linear interpolation
|
|
533
|
+
return startValue + (endValue - startValue) * t;
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Transition event handlers
|
|
538
|
+
function createTransitionWithEvents(element: any, config: TransitionEventConfig): TransitionWithEvents {
|
|
539
|
+
const {
|
|
540
|
+
duration = transitionConfig.defaultDuration,
|
|
541
|
+
onStart,
|
|
542
|
+
onEnd,
|
|
543
|
+
onInterrupt
|
|
544
|
+
} = config;
|
|
545
|
+
|
|
546
|
+
let isInterrupted = false;
|
|
547
|
+
|
|
548
|
+
const transition: TransitionWithEvents = {
|
|
549
|
+
start(): TransitionWithEvents {
|
|
550
|
+
if (onStart) onStart();
|
|
551
|
+
return this;
|
|
552
|
+
},
|
|
553
|
+
|
|
554
|
+
interrupt(): TransitionWithEvents {
|
|
555
|
+
isInterrupted = true;
|
|
556
|
+
if (onInterrupt) onInterrupt();
|
|
557
|
+
return this;
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
then(callback?: () => void): TransitionWithEvents {
|
|
561
|
+
if (!isInterrupted && onEnd) {
|
|
562
|
+
setTimeout(() => {
|
|
563
|
+
onEnd();
|
|
564
|
+
if (callback) callback();
|
|
565
|
+
}, duration);
|
|
566
|
+
}
|
|
567
|
+
return this;
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
return transition;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const animationSystem: AnimationSystem = {
|
|
575
|
+
easingFunctions,
|
|
576
|
+
animateValue,
|
|
577
|
+
staggeredAnimation,
|
|
578
|
+
morphShape,
|
|
579
|
+
fadeTransition,
|
|
580
|
+
slideTransition,
|
|
581
|
+
scaleTransition,
|
|
582
|
+
createTransitionSequence,
|
|
583
|
+
createSpringAnimation,
|
|
584
|
+
createStaggeredTransition,
|
|
585
|
+
createCustomTween,
|
|
586
|
+
createTransitionWithEvents,
|
|
587
|
+
transitionConfig,
|
|
588
|
+
presets
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
return animationSystem;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Create a global animation system instance
|
|
595
|
+
export const animationSystem = createAnimationSystem();
|