canvasframework 0.5.48 → 0.5.50
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/core/CanvasFramework.js +269 -91
- package/package.json +1 -1
package/core/CanvasFramework.js
CHANGED
|
@@ -149,6 +149,213 @@ const FIXED_COMPONENT_TYPES = new Set([
|
|
|
149
149
|
* @property {number} scrollVelocity - Vitesse de défilement
|
|
150
150
|
* @property {number} scrollFriction - Friction du défilement
|
|
151
151
|
*/
|
|
152
|
+
|
|
153
|
+
// ========================================
|
|
154
|
+
// ✨ NOUVEAU: WorkerPool System
|
|
155
|
+
// ========================================
|
|
156
|
+
|
|
157
|
+
class WorkerPool {
|
|
158
|
+
constructor(options = {}) {
|
|
159
|
+
this.maxWorkers = options.maxWorkers || navigator.hardwareConcurrency || 4;
|
|
160
|
+
this.minWorkers = options.minWorkers || 1;
|
|
161
|
+
this.workerScript = options.workerScript || null;
|
|
162
|
+
|
|
163
|
+
this.workers = [];
|
|
164
|
+
this.availableWorkers = [];
|
|
165
|
+
this.busyWorkers = new Set();
|
|
166
|
+
this.taskQueue = [];
|
|
167
|
+
this.taskIdCounter = 0;
|
|
168
|
+
this.pendingTasks = new Map();
|
|
169
|
+
|
|
170
|
+
this._initializeMinWorkers();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
_initializeMinWorkers() {
|
|
174
|
+
for (let i = 0; i < this.minWorkers; i++) {
|
|
175
|
+
this._createWorker();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
_createWorker() {
|
|
180
|
+
const blob = new Blob([this._getDefaultWorkerCode()], { type: 'application/javascript' });
|
|
181
|
+
const workerUrl = URL.createObjectURL(blob);
|
|
182
|
+
const worker = new Worker(workerUrl);
|
|
183
|
+
const workerWrapper = this._wrapWorker(worker);
|
|
184
|
+
|
|
185
|
+
this.workers.push(workerWrapper);
|
|
186
|
+
this.availableWorkers.push(workerWrapper);
|
|
187
|
+
URL.revokeObjectURL(workerUrl);
|
|
188
|
+
|
|
189
|
+
return workerWrapper;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
_wrapWorker(worker) {
|
|
193
|
+
const wrapper = {
|
|
194
|
+
worker,
|
|
195
|
+
id: `worker-${Date.now()}-${Math.random()}`,
|
|
196
|
+
currentTask: null,
|
|
197
|
+
lastUsed: Date.now()
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
worker.onmessage = (e) => this._handleWorkerMessage(wrapper, e);
|
|
201
|
+
worker.onerror = (e) => this._handleWorkerError(wrapper, e);
|
|
202
|
+
|
|
203
|
+
return wrapper;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
_getDefaultWorkerCode() {
|
|
207
|
+
return `
|
|
208
|
+
let state = {};
|
|
209
|
+
|
|
210
|
+
self.onmessage = async function(e) {
|
|
211
|
+
const { taskId, type, payload } = e.data;
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
let result;
|
|
215
|
+
|
|
216
|
+
switch(type) {
|
|
217
|
+
case 'SET_STATE':
|
|
218
|
+
state = payload;
|
|
219
|
+
result = { state };
|
|
220
|
+
break;
|
|
221
|
+
|
|
222
|
+
case 'EXECUTE':
|
|
223
|
+
const fn = new Function('state', 'args', payload.fnString);
|
|
224
|
+
result = await fn(state, payload.args);
|
|
225
|
+
break;
|
|
226
|
+
|
|
227
|
+
case 'COMPUTE':
|
|
228
|
+
result = payload.data;
|
|
229
|
+
break;
|
|
230
|
+
|
|
231
|
+
default:
|
|
232
|
+
throw new Error('Unknown task type: ' + type);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
self.postMessage({
|
|
236
|
+
taskId,
|
|
237
|
+
type: 'SUCCESS',
|
|
238
|
+
result
|
|
239
|
+
});
|
|
240
|
+
} catch (error) {
|
|
241
|
+
self.postMessage({
|
|
242
|
+
taskId,
|
|
243
|
+
type: 'ERROR',
|
|
244
|
+
error: error.message
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
_handleWorkerMessage(wrapper, e) {
|
|
252
|
+
const { taskId, type, result, error } = e.data;
|
|
253
|
+
const task = this.pendingTasks.get(taskId);
|
|
254
|
+
|
|
255
|
+
if (!task) return;
|
|
256
|
+
|
|
257
|
+
this.pendingTasks.delete(taskId);
|
|
258
|
+
this.busyWorkers.delete(wrapper);
|
|
259
|
+
this.availableWorkers.push(wrapper);
|
|
260
|
+
wrapper.currentTask = null;
|
|
261
|
+
wrapper.lastUsed = Date.now();
|
|
262
|
+
|
|
263
|
+
if (type === 'ERROR') {
|
|
264
|
+
task.reject(new Error(error));
|
|
265
|
+
} else {
|
|
266
|
+
task.resolve(result);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
this._processQueue();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
_handleWorkerError(wrapper, error) {
|
|
273
|
+
console.error('Worker error:', error);
|
|
274
|
+
|
|
275
|
+
if (wrapper.currentTask) {
|
|
276
|
+
const task = this.pendingTasks.get(wrapper.currentTask);
|
|
277
|
+
if (task) {
|
|
278
|
+
task.reject(error);
|
|
279
|
+
this.pendingTasks.delete(wrapper.currentTask);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
this.busyWorkers.delete(wrapper);
|
|
284
|
+
const index = this.workers.indexOf(wrapper);
|
|
285
|
+
if (index > -1) {
|
|
286
|
+
this.workers.splice(index, 1);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (this.workers.length < this.minWorkers) {
|
|
290
|
+
this._createWorker();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
this._processQueue();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
execute(type, payload) {
|
|
297
|
+
return new Promise((resolve, reject) => {
|
|
298
|
+
const taskId = ++this.taskIdCounter;
|
|
299
|
+
|
|
300
|
+
this.taskQueue.push({
|
|
301
|
+
taskId,
|
|
302
|
+
type,
|
|
303
|
+
payload,
|
|
304
|
+
resolve,
|
|
305
|
+
reject,
|
|
306
|
+
timestamp: Date.now()
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
this._processQueue();
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
_processQueue() {
|
|
314
|
+
while (this.taskQueue.length > 0 && this.availableWorkers.length > 0) {
|
|
315
|
+
const task = this.taskQueue.shift();
|
|
316
|
+
const wrapper = this.availableWorkers.shift();
|
|
317
|
+
|
|
318
|
+
this.busyWorkers.add(wrapper);
|
|
319
|
+
wrapper.currentTask = task.taskId;
|
|
320
|
+
this.pendingTasks.set(task.taskId, task);
|
|
321
|
+
|
|
322
|
+
wrapper.worker.postMessage({
|
|
323
|
+
taskId: task.taskId,
|
|
324
|
+
type: task.type,
|
|
325
|
+
payload: task.payload
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (this.taskQueue.length > 0 &&
|
|
330
|
+
this.availableWorkers.length === 0 &&
|
|
331
|
+
this.workers.length < this.maxWorkers) {
|
|
332
|
+
this._createWorker();
|
|
333
|
+
this._processQueue();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
getStats() {
|
|
338
|
+
return {
|
|
339
|
+
totalWorkers: this.workers.length,
|
|
340
|
+
availableWorkers: this.availableWorkers.length,
|
|
341
|
+
busyWorkers: this.busyWorkers.size,
|
|
342
|
+
queuedTasks: this.taskQueue.length,
|
|
343
|
+
pendingTasks: this.pendingTasks.size
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
terminateAll() {
|
|
348
|
+
this.workers.forEach(wrapper => {
|
|
349
|
+
wrapper.worker.terminate();
|
|
350
|
+
});
|
|
351
|
+
this.workers = [];
|
|
352
|
+
this.availableWorkers = [];
|
|
353
|
+
this.busyWorkers.clear();
|
|
354
|
+
this.taskQueue = [];
|
|
355
|
+
this.pendingTasks.clear();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
152
359
|
class CanvasFramework {
|
|
153
360
|
/**
|
|
154
361
|
* Crée une instance de CanvasFramework
|
|
@@ -316,21 +523,14 @@ class CanvasFramework {
|
|
|
316
523
|
}
|
|
317
524
|
});
|
|
318
525
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
// Envoyer l'état initial au worker
|
|
329
|
-
this.logicWorker.postMessage({
|
|
330
|
-
type: 'SET_STATE',
|
|
331
|
-
payload: this.state
|
|
332
|
-
});
|
|
333
|
-
|
|
526
|
+
// WorkerPool pour le logic
|
|
527
|
+
this.workerPool = new WorkerPool({
|
|
528
|
+
maxWorkers: options.maxLogicWorkers || navigator.hardwareConcurrency || 4,
|
|
529
|
+
minWorkers: options.minLogicWorkers || 1
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
this.logicWorkerState = {};
|
|
533
|
+
|
|
334
534
|
// Gestion des événements
|
|
335
535
|
this.isDragging = false;
|
|
336
536
|
this.lastTouchY = 0;
|
|
@@ -1483,37 +1683,7 @@ class CanvasFramework {
|
|
|
1483
1683
|
return new Worker(URL.createObjectURL(blob));
|
|
1484
1684
|
}
|
|
1485
1685
|
|
|
1486
|
-
createLogicWorker() {
|
|
1487
|
-
const workerCode = `
|
|
1488
|
-
let state = {};
|
|
1489
1686
|
|
|
1490
|
-
self.onmessage = async function(e) {
|
|
1491
|
-
const { type, payload } = e.data;
|
|
1492
|
-
|
|
1493
|
-
switch(type) {
|
|
1494
|
-
case 'SET_STATE':
|
|
1495
|
-
state = payload;
|
|
1496
|
-
self.postMessage({ type: 'STATE_UPDATED', payload: state });
|
|
1497
|
-
break;
|
|
1498
|
-
|
|
1499
|
-
case 'EXECUTE':
|
|
1500
|
-
try {
|
|
1501
|
-
const fn = new Function('state', 'args', payload.fnString);
|
|
1502
|
-
const result = await fn(state, payload.args);
|
|
1503
|
-
self.postMessage({ type: 'EXECUTION_RESULT', payload: result });
|
|
1504
|
-
} catch (err) {
|
|
1505
|
-
self.postMessage({ type: 'EXECUTION_ERROR', payload: err.message });
|
|
1506
|
-
}
|
|
1507
|
-
break;
|
|
1508
|
-
}
|
|
1509
|
-
};
|
|
1510
|
-
`;
|
|
1511
|
-
|
|
1512
|
-
const blob = new Blob([workerCode], {
|
|
1513
|
-
type: 'application/javascript'
|
|
1514
|
-
});
|
|
1515
|
-
return new Worker(URL.createObjectURL(blob));
|
|
1516
|
-
}
|
|
1517
1687
|
|
|
1518
1688
|
// Set Theme dynamique
|
|
1519
1689
|
setTheme(theme) {
|
|
@@ -1588,49 +1758,39 @@ class CanvasFramework {
|
|
|
1588
1758
|
});
|
|
1589
1759
|
}
|
|
1590
1760
|
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
case 'EXECUTION_RESULT':
|
|
1604
|
-
// Résultat d'une tâche spécifique envoyée au worker
|
|
1605
|
-
if (this.onWorkerResult) this.onWorkerResult(payload);
|
|
1606
|
-
break;
|
|
1607
|
-
|
|
1608
|
-
case 'EXECUTION_ERROR':
|
|
1609
|
-
console.error('Logic Worker Error:', payload);
|
|
1610
|
-
break;
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Exécute une tâche dans le pool de workers
|
|
1763
|
+
* @param {string} fnString - Code de la fonction à exécuter
|
|
1764
|
+
* @param {*} args - Arguments pour la fonction
|
|
1765
|
+
* @returns {Promise} Résultat de l'exécution
|
|
1766
|
+
*/
|
|
1767
|
+
async executeTask(fnString, args = {}) {
|
|
1768
|
+
return this.workerPool.execute('EXECUTE', {
|
|
1769
|
+
fnString,
|
|
1770
|
+
args
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1613
1773
|
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1774
|
+
/**
|
|
1775
|
+
* Met à jour l'état dans tous les workers
|
|
1776
|
+
* @param {Object} newState - Nouvel état
|
|
1777
|
+
*/
|
|
1778
|
+
async updateWorkerState(newState) {
|
|
1779
|
+
this.logicWorkerState = {
|
|
1780
|
+
...this.logicWorkerState,
|
|
1781
|
+
...newState
|
|
1782
|
+
};
|
|
1783
|
+
|
|
1784
|
+
return this.workerPool.execute('SET_STATE', this.logicWorkerState);
|
|
1785
|
+
}
|
|
1623
1786
|
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
payload: this.logicWorkerState
|
|
1632
|
-
});
|
|
1633
|
-
}
|
|
1787
|
+
/**
|
|
1788
|
+
* Obtient les statistiques du pool de workers
|
|
1789
|
+
* @returns {Object} Statistiques
|
|
1790
|
+
*/
|
|
1791
|
+
getWorkerPoolStats() {
|
|
1792
|
+
return this.workerPool.getStats();
|
|
1793
|
+
}
|
|
1634
1794
|
|
|
1635
1795
|
detectPlatform() {
|
|
1636
1796
|
const ua = navigator.userAgent.toLowerCase();
|
|
@@ -1846,8 +2006,24 @@ class CanvasFramework {
|
|
|
1846
2006
|
comp.destroy();
|
|
1847
2007
|
}
|
|
1848
2008
|
}
|
|
2009
|
+
|
|
2010
|
+
// 2. Désactiver TOUS les gestionnaires d'événements
|
|
2011
|
+
comp.onClick = null;
|
|
2012
|
+
comp.onPress = null;
|
|
2013
|
+
comp.onMove = null;
|
|
2014
|
+
comp.onHover = null;
|
|
2015
|
+
comp.onFocus = null;
|
|
2016
|
+
comp.onBlur = null;
|
|
2017
|
+
comp.onChange = null;
|
|
2018
|
+
comp.onInput = null;
|
|
2019
|
+
comp.onSubmit = null;
|
|
2020
|
+
|
|
2021
|
+
if (comp._unmount && typeof comp._unmount === 'function') {
|
|
2022
|
+
comp._unmount();
|
|
2023
|
+
}
|
|
1849
2024
|
});
|
|
1850
|
-
|
|
2025
|
+
|
|
2026
|
+
|
|
1851
2027
|
// ✅ Nettoyer toutes les vidéos orphelines AVANT de créer les nouveaux composants
|
|
1852
2028
|
const allVideos = document.querySelectorAll('video');
|
|
1853
2029
|
allVideos.forEach(v => v.remove());
|
|
@@ -2985,12 +3161,14 @@ class CanvasFramework {
|
|
|
2985
3161
|
if (this.scrollWorker) {
|
|
2986
3162
|
this.scrollWorker.terminate();
|
|
2987
3163
|
}
|
|
2988
|
-
|
|
3164
|
+
|
|
3165
|
+
if (this.worker) {
|
|
2989
3166
|
this.worker.terminate();
|
|
2990
3167
|
}
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
3168
|
+
|
|
3169
|
+
if (this.workerPool) {
|
|
3170
|
+
this.workerPool.terminateAll();
|
|
3171
|
+
}
|
|
2994
3172
|
|
|
2995
3173
|
if (this.ctx && typeof this.ctx.destroy === 'function') {
|
|
2996
3174
|
this.ctx.destroy();
|