canvasframework 0.5.18 → 0.5.19

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 (112) hide show
  1. package/components/Accordion.js +265 -0
  2. package/components/AndroidDatePickerDialog.js +406 -0
  3. package/components/AppBar.js +398 -0
  4. package/components/AudioPlayer.js +611 -0
  5. package/components/Avatar.js +202 -0
  6. package/components/Banner.js +342 -0
  7. package/components/BottomNavigationBar.js +433 -0
  8. package/components/BottomSheet.js +234 -0
  9. package/components/Button.js +358 -0
  10. package/components/Camera.js +644 -0
  11. package/components/Card.js +193 -0
  12. package/components/Chart.js +700 -0
  13. package/components/Checkbox.js +166 -0
  14. package/components/Chip.js +212 -0
  15. package/components/CircularProgress.js +327 -0
  16. package/components/ContextMenu.js +116 -0
  17. package/components/DatePicker.js +298 -0
  18. package/components/Dialog.js +337 -0
  19. package/components/Divider.js +125 -0
  20. package/components/Drawer.js +276 -0
  21. package/components/FAB.js +270 -0
  22. package/components/FileUpload.js +315 -0
  23. package/components/FloatedCamera.js +644 -0
  24. package/components/IOSDatePickerWheel.js +430 -0
  25. package/components/ImageCarousel.js +219 -0
  26. package/components/ImageComponent.js +223 -0
  27. package/components/Input.js +831 -0
  28. package/components/InputDatalist.js +723 -0
  29. package/components/InputTags.js +624 -0
  30. package/components/List.js +95 -0
  31. package/components/ListItem.js +269 -0
  32. package/components/Modal.js +364 -0
  33. package/components/MorphingFAB.js +428 -0
  34. package/components/MultiSelectDialog.js +206 -0
  35. package/components/NumberInput.js +271 -0
  36. package/components/PasswordInput.js +462 -0
  37. package/components/ProgressBar.js +88 -0
  38. package/components/QRCodeReader.js +539 -0
  39. package/components/RadioButton.js +151 -0
  40. package/components/SearchInput.js +315 -0
  41. package/components/SegmentedControl.js +357 -0
  42. package/components/Select.js +199 -0
  43. package/components/SelectDialog.js +255 -0
  44. package/components/Slider.js +113 -0
  45. package/components/SliverAppBar.js +139 -0
  46. package/components/Snackbar.js +243 -0
  47. package/components/SpeedDialFAB.js +397 -0
  48. package/components/Stepper.js +281 -0
  49. package/components/SwipeableListItem.js +327 -0
  50. package/components/Switch.js +147 -0
  51. package/components/Table.js +492 -0
  52. package/components/Tabs.js +423 -0
  53. package/components/Text.js +141 -0
  54. package/components/TextField.js +151 -0
  55. package/components/TimePicker.js +934 -0
  56. package/components/Toast.js +236 -0
  57. package/components/TreeView.js +420 -0
  58. package/components/Video.js +397 -0
  59. package/components/View.js +140 -0
  60. package/components/VirtualList.js +120 -0
  61. package/core/CanvasFramework.js +3045 -0
  62. package/core/Component.js +243 -0
  63. package/core/ThemeManager.js +358 -0
  64. package/core/UIBuilder.js +267 -0
  65. package/core/WebGLCanvasAdapter.js +782 -0
  66. package/features/Column.js +43 -0
  67. package/features/Grid.js +47 -0
  68. package/features/LayoutComponent.js +43 -0
  69. package/features/OpenStreetMap.js +310 -0
  70. package/features/Positioned.js +33 -0
  71. package/features/PullToRefresh.js +328 -0
  72. package/features/Row.js +40 -0
  73. package/features/SignaturePad.js +257 -0
  74. package/features/Skeleton.js +193 -0
  75. package/features/Stack.js +21 -0
  76. package/index.js +119 -0
  77. package/manager/AccessibilityManager.js +107 -0
  78. package/manager/ErrorHandler.js +59 -0
  79. package/manager/FeatureFlags.js +60 -0
  80. package/manager/MemoryManager.js +107 -0
  81. package/manager/PerformanceMonitor.js +84 -0
  82. package/manager/SecurityManager.js +54 -0
  83. package/package.json +22 -16
  84. package/utils/AnimationEngine.js +734 -0
  85. package/utils/CryptoManager.js +303 -0
  86. package/utils/DataStore.js +403 -0
  87. package/utils/DevTools.js +1618 -0
  88. package/utils/DevToolsConsole.js +201 -0
  89. package/utils/EventBus.js +407 -0
  90. package/utils/FetchClient.js +74 -0
  91. package/utils/FirebaseAuth.js +653 -0
  92. package/utils/FirebaseCore.js +246 -0
  93. package/utils/FirebaseFirestore.js +581 -0
  94. package/utils/FirebaseFunctions.js +97 -0
  95. package/utils/FirebaseRealtimeDB.js +498 -0
  96. package/utils/FirebaseStorage.js +612 -0
  97. package/utils/FormValidator.js +355 -0
  98. package/utils/GeoLocationService.js +62 -0
  99. package/utils/I18n.js +207 -0
  100. package/utils/IndexedDBManager.js +273 -0
  101. package/utils/InspectionOverlay.js +308 -0
  102. package/utils/NotificationManager.js +60 -0
  103. package/utils/OfflineSyncManager.js +342 -0
  104. package/utils/PayPalPayment.js +678 -0
  105. package/utils/QueryBuilder.js +478 -0
  106. package/utils/SafeArea.js +64 -0
  107. package/utils/SecureStorage.js +289 -0
  108. package/utils/StateManager.js +207 -0
  109. package/utils/StripePayment.js +552 -0
  110. package/utils/WebSocketClient.js +66 -0
  111. package/dist/canvasframework.js +0 -2
  112. package/dist/canvasframework.js.LICENSE.txt +0 -1
@@ -0,0 +1,734 @@
1
+ /**
2
+ * Moteur d'animation pour composants Canvas
3
+ * Permet d'animer n'importe quelle propriété d'un composant sans le modifier
4
+ * @class
5
+ * @property {Map} animations - Liste des animations actives
6
+ * @property {boolean} isRunning - Indique si le moteur tourne
7
+ */
8
+ class AnimationEngine {
9
+ constructor() {
10
+ this.animations = new Map();
11
+ this.isRunning = false;
12
+ this.animationFrameId = null;
13
+
14
+ // Batching
15
+ this.batchQueue = [];
16
+ this.batchScheduled = false;
17
+
18
+ // Worker pour les calculs d'animation
19
+ this.worker = null;
20
+ this.workerEnabled = true;
21
+ this.initWorker();
22
+
23
+ // OffscreenCanvas cache
24
+ this.offscreenCache = new Map();
25
+ }
26
+
27
+ /**
28
+ * Initialise le Web Worker pour les animations
29
+ * @private
30
+ */
31
+ initWorker() {
32
+ if (!window.Worker) {
33
+ this.workerEnabled = false;
34
+ console.warn('Web Workers non supportés, fallback sur le thread principal');
35
+ return;
36
+ }
37
+
38
+ // Créer le worker inline
39
+ const workerCode = `
40
+ // Fonctions d'easing dans le Worker
41
+ const easings = {
42
+ linear: t => t,
43
+ easeInQuad: t => t * t,
44
+ easeOutQuad: t => t * (2 - t),
45
+ easeInOutQuad: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
46
+ easeInCubic: t => t * t * t,
47
+ easeOutCubic: t => (--t) * t * t + 1,
48
+ easeInOutCubic: t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
49
+ easeInQuart: t => t * t * t * t,
50
+ easeOutQuart: t => 1 - (--t) * t * t * t,
51
+ easeInOutQuart: t => t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t,
52
+ easeInElastic: t => {
53
+ const c4 = (2 * Math.PI) / 3;
54
+ return t === 0 ? 0 : t === 1 ? 1 : -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * c4);
55
+ },
56
+ easeOutElastic: t => {
57
+ const c4 = (2 * Math.PI) / 3;
58
+ return t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
59
+ },
60
+ easeOutBounce: t => {
61
+ const n1 = 7.5625;
62
+ const d1 = 2.75;
63
+ if (t < 1 / d1) return n1 * t * t;
64
+ else if (t < 2 / d1) return n1 * (t -= 1.5 / d1) * t + 0.75;
65
+ else if (t < 2.5 / d1) return n1 * (t -= 2.25 / d1) * t + 0.9375;
66
+ else return n1 * (t -= 2.625 / d1) * t + 0.984375;
67
+ }
68
+ };
69
+
70
+ // Calculer les valeurs d'animation
71
+ self.onmessage = function(e) {
72
+ const { type, data } = e.data;
73
+
74
+ if (type === 'CALCULATE_BATCH') {
75
+ const { animations, currentTime } = data;
76
+ const results = [];
77
+
78
+ for (let anim of animations) {
79
+ // Gérer le délai
80
+ if (anim.isDelaying) {
81
+ if (!anim.delayStartTime) {
82
+ anim.delayStartTime = currentTime;
83
+ }
84
+
85
+ if (currentTime - anim.delayStartTime >= anim.delay) {
86
+ anim.isDelaying = false;
87
+ anim.startTime = currentTime;
88
+ } else {
89
+ results.push({ id: anim.id, status: 'delaying', animation: anim });
90
+ continue;
91
+ }
92
+ }
93
+
94
+ // Initialiser le temps de départ
95
+ if (!anim.startTime) {
96
+ anim.startTime = currentTime;
97
+ }
98
+
99
+ // Calculer la progression
100
+ const elapsed = currentTime - anim.startTime;
101
+ let progress = Math.min(elapsed / anim.duration, 1);
102
+
103
+ // Appliquer l'easing
104
+ const easingFunc = easings[anim.easing] || easings.linear;
105
+ const easedProgress = easingFunc(progress);
106
+
107
+ // Inverser si yoyo
108
+ const actualProgress = anim.isReversed ? 1 - easedProgress : easedProgress;
109
+
110
+ // Calculer les nouvelles valeurs
111
+ const values = {};
112
+ for (let prop in anim.to) {
113
+ const from = anim.from[prop];
114
+ const to = anim.to[prop];
115
+ values[prop] = from + (to - from) * actualProgress;
116
+ }
117
+
118
+ // Déterminer le statut
119
+ let status = 'running';
120
+ let newState = { ...anim };
121
+
122
+ if (progress >= 1) {
123
+ if (anim.yoyo && !anim.isReversed) {
124
+ status = 'yoyo';
125
+ newState.isReversed = true;
126
+ newState.startTime = currentTime;
127
+ } else if (anim.loop) {
128
+ status = 'loop';
129
+ newState.startTime = currentTime;
130
+ newState.isReversed = false;
131
+ } else {
132
+ status = 'complete';
133
+ }
134
+ }
135
+
136
+ results.push({
137
+ id: anim.id,
138
+ status,
139
+ values,
140
+ actualProgress,
141
+ animation: newState
142
+ });
143
+ }
144
+
145
+ self.postMessage({ type: 'BATCH_RESULT', results });
146
+ }
147
+ };
148
+ `;
149
+
150
+ const blob = new Blob([workerCode], { type: 'application/javascript' });
151
+ const workerUrl = URL.createObjectURL(blob);
152
+
153
+ try {
154
+ this.worker = new Worker(workerUrl);
155
+
156
+ this.worker.onmessage = (e) => {
157
+ if (e.data.type === 'BATCH_RESULT') {
158
+ this.applyBatchResults(e.data.results);
159
+ }
160
+ };
161
+
162
+ this.worker.onerror = (error) => {
163
+ console.error('Erreur Worker:', error);
164
+ this.workerEnabled = false;
165
+ };
166
+ } catch (error) {
167
+ console.error('Impossible de créer le Worker:', error);
168
+ this.workerEnabled = false;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Applique les résultats calculés par le Worker
174
+ * @private
175
+ */
176
+ applyBatchResults(results) {
177
+ const toDelete = [];
178
+
179
+ for (let result of results) {
180
+ const anim = this.animations.get(result.id);
181
+ if (!anim) continue;
182
+
183
+ if (result.status === 'delaying') {
184
+ // Mettre à jour l'état de délai
185
+ Object.assign(anim, result.animation);
186
+ continue;
187
+ }
188
+
189
+ // Mettre à jour les propriétés du composant
190
+ if (result.values) {
191
+ for (let prop in result.values) {
192
+ anim.component[prop] = result.values[prop];
193
+ }
194
+ }
195
+
196
+ // Callback onUpdate
197
+ if (anim.onUpdate) {
198
+ anim.onUpdate(result.actualProgress);
199
+ }
200
+
201
+ // Gérer les états
202
+ if (result.status === 'yoyo' || result.status === 'loop') {
203
+ Object.assign(anim, result.animation);
204
+ } else if (result.status === 'complete') {
205
+ if (anim.onComplete) {
206
+ anim.onComplete();
207
+ }
208
+ toDelete.push(result.id);
209
+ }
210
+ }
211
+
212
+ // Nettoyer les animations terminées
213
+ toDelete.forEach(id => this.stop(id));
214
+ }
215
+
216
+ /**
217
+ * Crée un OffscreenCanvas pour pré-rendre un composant
218
+ * @param {string} cacheKey - Clé de cache
219
+ * @param {number} width - Largeur
220
+ * @param {number} height - Hauteur
221
+ * @param {Function} renderFn - Fonction de rendu
222
+ */
223
+ createOffscreenCache(cacheKey, width, height, renderFn) {
224
+ if (!window.OffscreenCanvas) {
225
+ console.warn('OffscreenCanvas non supporté');
226
+ return null;
227
+ }
228
+
229
+ const offscreen = new OffscreenCanvas(width, height);
230
+ const ctx = offscreen.getContext('2d');
231
+
232
+ renderFn(ctx);
233
+
234
+ this.offscreenCache.set(cacheKey, offscreen);
235
+ return offscreen;
236
+ }
237
+
238
+ /**
239
+ * Récupère un OffscreenCanvas du cache
240
+ * @param {string} cacheKey - Clé de cache
241
+ */
242
+ getOffscreenCache(cacheKey) {
243
+ return this.offscreenCache.get(cacheKey);
244
+ }
245
+
246
+ /**
247
+ * Nettoie le cache OffscreenCanvas
248
+ * @param {string} cacheKey - Clé de cache (optionnel)
249
+ */
250
+ clearOffscreenCache(cacheKey = null) {
251
+ if (cacheKey) {
252
+ this.offscreenCache.delete(cacheKey);
253
+ } else {
254
+ this.offscreenCache.clear();
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Anime une propriété d'un composant
260
+ * @param {Component} component - Composant à animer
261
+ * @param {Object} options - Options d'animation
262
+ * @param {Object} options.from - Valeurs de départ {x: 0, y: 0, ...}
263
+ * @param {Object} options.to - Valeurs d'arrivée {x: 100, y: 200, ...}
264
+ * @param {number} options.duration - Durée en ms (défaut: 300)
265
+ * @param {string} options.easing - Fonction d'easing (défaut: 'easeInOutQuad')
266
+ * @param {number} options.delay - Délai avant démarrage en ms (défaut: 0)
267
+ * @param {Function} options.onUpdate - Callback à chaque frame
268
+ * @param {Function} options.onComplete - Callback à la fin
269
+ * @param {boolean} options.loop - Boucler l'animation (défaut: false)
270
+ * @param {boolean} options.yoyo - Retour inverse (défaut: false)
271
+ * @returns {string} ID de l'animation
272
+ */
273
+ animate(component, options = {}) {
274
+ const animationId = this.generateId();
275
+
276
+ const animation = {
277
+ id: animationId,
278
+ component,
279
+ from: options.from || {},
280
+ to: options.to || {},
281
+ duration: options.duration || 300,
282
+ easing: options.easing || 'easeInOutQuad',
283
+ delay: options.delay || 0,
284
+ onUpdate: options.onUpdate,
285
+ onComplete: options.onComplete,
286
+ loop: options.loop || false,
287
+ yoyo: options.yoyo || false,
288
+
289
+ // État interne
290
+ startTime: null,
291
+ delayStartTime: null,
292
+ progress: 0,
293
+ isDelaying: options.delay > 0,
294
+ isReversed: false,
295
+ originalValues: {}
296
+ };
297
+
298
+ // Sauvegarder les valeurs originales
299
+ for (let prop in animation.to) {
300
+ if (animation.from[prop] === undefined) {
301
+ animation.from[prop] = component[prop];
302
+ }
303
+ animation.originalValues[prop] = component[prop];
304
+ }
305
+
306
+ this.animations.set(animationId, animation);
307
+
308
+ if (!this.isRunning) {
309
+ this.start();
310
+ }
311
+
312
+ return animationId;
313
+ }
314
+
315
+ /**
316
+ * Anime plusieurs propriétés en séquence
317
+ * @param {Component} component - Composant à animer
318
+ * @param {Array} sequence - Tableau d'options d'animation
319
+ * @returns {Promise} Promesse résolue à la fin de la séquence
320
+ */
321
+ async animateSequence(component, sequence) {
322
+ for (let options of sequence) {
323
+ await new Promise(resolve => {
324
+ this.animate(component, {
325
+ ...options,
326
+ onComplete: () => {
327
+ if (options.onComplete) options.onComplete();
328
+ resolve();
329
+ }
330
+ });
331
+ });
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Anime plusieurs composants en parallèle
337
+ * @param {Array} animations - Tableau de {component, options}
338
+ * @returns {Promise} Promesse résolue quand toutes sont finies
339
+ */
340
+ async animateParallel(animations) {
341
+ const promises = animations.map(({component, options}) => {
342
+ return new Promise(resolve => {
343
+ this.animate(component, {
344
+ ...options,
345
+ onComplete: () => {
346
+ if (options.onComplete) options.onComplete();
347
+ resolve();
348
+ }
349
+ });
350
+ });
351
+ });
352
+
353
+ return Promise.all(promises);
354
+ }
355
+
356
+ /**
357
+ * Crée une animation de rebond
358
+ * @param {Component} component - Composant
359
+ * @param {Object} options - Options
360
+ */
361
+ bounce(component, options = {}) {
362
+ const originalY = component.y;
363
+ const height = options.height || 50;
364
+
365
+ return this.animate(component, {
366
+ from: { y: originalY },
367
+ to: { y: originalY - height },
368
+ duration: options.duration || 400,
369
+ easing: 'easeOutQuad',
370
+ yoyo: true,
371
+ onComplete: options.onComplete
372
+ });
373
+ }
374
+
375
+ /**
376
+ * Crée une animation de shake (tremblement)
377
+ * @param {Component} component - Composant
378
+ * @param {Object} options - Options
379
+ */
380
+ shake(component, options = {}) {
381
+ const originalX = component.x;
382
+ const intensity = options.intensity || 10;
383
+ const shakes = options.shakes || 4;
384
+ const duration = options.duration || 400;
385
+
386
+ const sequence = [];
387
+ for (let i = 0; i < shakes; i++) {
388
+ sequence.push({
389
+ to: { x: originalX + (i % 2 === 0 ? intensity : -intensity) },
390
+ duration: duration / (shakes * 2),
391
+ easing: 'linear'
392
+ });
393
+ }
394
+ sequence.push({
395
+ to: { x: originalX },
396
+ duration: duration / (shakes * 2),
397
+ easing: 'linear',
398
+ onComplete: options.onComplete
399
+ });
400
+
401
+ return this.animateSequence(component, sequence);
402
+ }
403
+
404
+ /**
405
+ * Crée une animation de pulsation (scale)
406
+ * @param {Component} component - Composant
407
+ * @param {Object} options - Options
408
+ */
409
+ pulse(component, options = {}) {
410
+ const originalWidth = component.width;
411
+ const originalHeight = component.height;
412
+ const scale = options.scale || 1.1;
413
+
414
+ return this.animate(component, {
415
+ from: {
416
+ width: originalWidth,
417
+ height: originalHeight
418
+ },
419
+ to: {
420
+ width: originalWidth * scale,
421
+ height: originalHeight * scale
422
+ },
423
+ duration: options.duration || 300,
424
+ easing: 'easeInOutQuad',
425
+ yoyo: true,
426
+ loop: options.loop || false,
427
+ onComplete: options.onComplete
428
+ });
429
+ }
430
+
431
+ /**
432
+ * Crée une animation de fade (opacité)
433
+ * @param {Component} component - Composant
434
+ * @param {Object} options - Options
435
+ */
436
+ fade(component, options = {}) {
437
+ // Ajouter une propriété opacity si elle n'existe pas
438
+ if (component.opacity === undefined) {
439
+ component.opacity = 1;
440
+ }
441
+
442
+ return this.animate(component, {
443
+ from: { opacity: options.from !== undefined ? options.from : component.opacity },
444
+ to: { opacity: options.to !== undefined ? options.to : 0 },
445
+ duration: options.duration || 300,
446
+ easing: options.easing || 'linear',
447
+ onComplete: options.onComplete
448
+ });
449
+ }
450
+
451
+ /**
452
+ * Crée une animation de slide (glissement)
453
+ * @param {Component} component - Composant
454
+ * @param {Object} options - Options
455
+ */
456
+ slide(component, options = {}) {
457
+ return this.animate(component, {
458
+ from: options.from || { x: component.x, y: component.y },
459
+ to: options.to || { x: component.x + 100, y: component.y },
460
+ duration: options.duration || 400,
461
+ easing: options.easing || 'easeOutQuad',
462
+ onComplete: options.onComplete
463
+ });
464
+ }
465
+
466
+ /**
467
+ * Crée une animation de rotation
468
+ * @param {Component} component - Composant
469
+ * @param {Object} options - Options
470
+ */
471
+ rotate(component, options = {}) {
472
+ if (component.rotation === undefined) {
473
+ component.rotation = 0;
474
+ }
475
+
476
+ return this.animate(component, {
477
+ from: { rotation: options.from !== undefined ? options.from : component.rotation },
478
+ to: { rotation: options.to !== undefined ? options.to : 360 },
479
+ duration: options.duration || 1000,
480
+ easing: options.easing || 'linear',
481
+ loop: options.loop || false,
482
+ onComplete: options.onComplete
483
+ });
484
+ }
485
+
486
+ /**
487
+ * Arrête une animation
488
+ * @param {string} animationId - ID de l'animation
489
+ */
490
+ stop(animationId) {
491
+ this.animations.delete(animationId);
492
+
493
+ if (this.animations.size === 0) {
494
+ this.isRunning = false;
495
+ if (this.animationFrameId) {
496
+ cancelAnimationFrame(this.animationFrameId);
497
+ this.animationFrameId = null;
498
+ }
499
+ }
500
+ }
501
+
502
+ /**
503
+ * Arrête toutes les animations d'un composant
504
+ * @param {Component} component - Composant
505
+ */
506
+ stopAll(component) {
507
+ const toDelete = [];
508
+
509
+ for (let [id, anim] of this.animations) {
510
+ if (anim.component === component) {
511
+ toDelete.push(id);
512
+ }
513
+ }
514
+
515
+ toDelete.forEach(id => this.stop(id));
516
+ }
517
+
518
+ /**
519
+ * Démarre le moteur d'animation
520
+ * @private
521
+ */
522
+ start() {
523
+ this.isRunning = true;
524
+ this.lastTime = performance.now();
525
+ this.update();
526
+ }
527
+
528
+ /**
529
+ * Met à jour toutes les animations (avec Worker et batching)
530
+ * @private
531
+ */
532
+ update() {
533
+ const currentTime = performance.now();
534
+
535
+ if (this.workerEnabled && this.worker) {
536
+ // Utiliser le Worker pour les calculs
537
+ this.updateWithWorker(currentTime);
538
+ } else {
539
+ // Fallback sur le thread principal
540
+ this.updateOnMainThread(currentTime);
541
+ }
542
+
543
+ // Continuer l'animation
544
+ if (this.animations.size > 0) {
545
+ this.animationFrameId = requestAnimationFrame(() => this.update());
546
+ } else {
547
+ this.isRunning = false;
548
+ }
549
+ }
550
+
551
+ /**
552
+ * Met à jour avec le Worker (batching automatique)
553
+ * @private
554
+ */
555
+ updateWithWorker(currentTime) {
556
+ const animationsArray = [];
557
+
558
+ for (let [id, anim] of this.animations) {
559
+ // Préparer les données pour le Worker (sans les callbacks et le composant)
560
+ animationsArray.push({
561
+ id: anim.id,
562
+ from: anim.from,
563
+ to: anim.to,
564
+ duration: anim.duration,
565
+ easing: anim.easing,
566
+ delay: anim.delay,
567
+ loop: anim.loop,
568
+ yoyo: anim.yoyo,
569
+ startTime: anim.startTime,
570
+ delayStartTime: anim.delayStartTime,
571
+ isDelaying: anim.isDelaying,
572
+ isReversed: anim.isReversed
573
+ });
574
+ }
575
+
576
+ if (animationsArray.length > 0) {
577
+ this.worker.postMessage({
578
+ type: 'CALCULATE_BATCH',
579
+ data: {
580
+ animations: animationsArray,
581
+ currentTime: currentTime
582
+ }
583
+ });
584
+ }
585
+ }
586
+
587
+ /**
588
+ * Met à jour sur le thread principal (fallback)
589
+ * @private
590
+ */
591
+ updateOnMainThread(currentTime) {
592
+ const toDelete = [];
593
+
594
+ for (let [id, anim] of this.animations) {
595
+ // Gérer le délai
596
+ if (anim.isDelaying) {
597
+ if (!anim.delayStartTime) {
598
+ anim.delayStartTime = currentTime;
599
+ }
600
+
601
+ if (currentTime - anim.delayStartTime >= anim.delay) {
602
+ anim.isDelaying = false;
603
+ anim.startTime = currentTime;
604
+ }
605
+ continue;
606
+ }
607
+
608
+ // Initialiser le temps de départ
609
+ if (!anim.startTime) {
610
+ anim.startTime = currentTime;
611
+ }
612
+
613
+ // Calculer la progression
614
+ const elapsed = currentTime - anim.startTime;
615
+ let progress = Math.min(elapsed / anim.duration, 1);
616
+
617
+ // Appliquer l'easing
618
+ const easedProgress = this.applyEasing(progress, anim.easing);
619
+
620
+ // Inverser si yoyo
621
+ const actualProgress = anim.isReversed ? 1 - easedProgress : easedProgress;
622
+
623
+ // Mettre à jour les propriétés du composant
624
+ for (let prop in anim.to) {
625
+ const from = anim.from[prop];
626
+ const to = anim.to[prop];
627
+ const value = from + (to - from) * actualProgress;
628
+ anim.component[prop] = value;
629
+ }
630
+
631
+ // Callback onUpdate
632
+ if (anim.onUpdate) {
633
+ anim.onUpdate(actualProgress);
634
+ }
635
+
636
+ // Animation terminée
637
+ if (progress >= 1) {
638
+ if (anim.yoyo && !anim.isReversed) {
639
+ // Inverser pour le yoyo
640
+ anim.isReversed = true;
641
+ anim.startTime = currentTime;
642
+ } else if (anim.loop) {
643
+ // Recommencer
644
+ anim.startTime = currentTime;
645
+ anim.isReversed = false;
646
+ } else {
647
+ // Terminer
648
+ if (anim.onComplete) {
649
+ anim.onComplete();
650
+ }
651
+ toDelete.push(id);
652
+ }
653
+ }
654
+ }
655
+
656
+ // Nettoyer les animations terminées
657
+ toDelete.forEach(id => this.stop(id));
658
+ }
659
+
660
+ /**
661
+ * Applique une fonction d'easing
662
+ * @param {number} t - Progression (0-1)
663
+ * @param {string} easingName - Nom de l'easing
664
+ * @returns {number} Valeur easée
665
+ * @private
666
+ */
667
+ applyEasing(t, easingName) {
668
+ const easings = {
669
+ linear: t => t,
670
+ easeInQuad: t => t * t,
671
+ easeOutQuad: t => t * (2 - t),
672
+ easeInOutQuad: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
673
+ easeInCubic: t => t * t * t,
674
+ easeOutCubic: t => (--t) * t * t + 1,
675
+ easeInOutCubic: t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
676
+ easeInQuart: t => t * t * t * t,
677
+ easeOutQuart: t => 1 - (--t) * t * t * t,
678
+ easeInOutQuart: t => t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t,
679
+ easeInElastic: t => {
680
+ const c4 = (2 * Math.PI) / 3;
681
+ return t === 0 ? 0 : t === 1 ? 1 : -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * c4);
682
+ },
683
+ easeOutElastic: t => {
684
+ const c4 = (2 * Math.PI) / 3;
685
+ return t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
686
+ },
687
+ easeOutBounce: t => {
688
+ const n1 = 7.5625;
689
+ const d1 = 2.75;
690
+ if (t < 1 / d1) return n1 * t * t;
691
+ else if (t < 2 / d1) return n1 * (t -= 1.5 / d1) * t + 0.75;
692
+ else if (t < 2.5 / d1) return n1 * (t -= 2.25 / d1) * t + 0.9375;
693
+ else return n1 * (t -= 2.625 / d1) * t + 0.984375;
694
+ }
695
+ };
696
+
697
+ return (easings[easingName] || easings.linear)(t);
698
+ }
699
+
700
+ /**
701
+ * Génère un ID unique
702
+ * @returns {string} ID unique
703
+ * @private
704
+ */
705
+ generateId() {
706
+ return `anim_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
707
+ }
708
+
709
+ /**
710
+ * Nettoie toutes les animations
711
+ */
712
+ clear() {
713
+ this.animations.clear();
714
+ this.isRunning = false;
715
+ if (this.animationFrameId) {
716
+ cancelAnimationFrame(this.animationFrameId);
717
+ this.animationFrameId = null;
718
+ }
719
+ this.clearOffscreenCache();
720
+ }
721
+
722
+ /**
723
+ * Nettoie le Worker
724
+ */
725
+ destroy() {
726
+ this.clear();
727
+ if (this.worker) {
728
+ this.worker.terminate();
729
+ this.worker = null;
730
+ }
731
+ }
732
+ }
733
+
734
+ export default AnimationEngine;