canvasframework 0.6.1 → 0.6.2
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 +94 -121
- package/core/WebGLCanvasAdapter.js +640 -641
- package/package.json +1 -1
package/core/CanvasFramework.js
CHANGED
|
@@ -381,6 +381,7 @@ class CanvasFramework {
|
|
|
381
381
|
};
|
|
382
382
|
this._firstRenderDone = false;
|
|
383
383
|
this._startupStartTime = startTime;
|
|
384
|
+
this._needsFullRender = true; // Flag de rendu global (remplace forEach sur scroll)
|
|
384
385
|
// Dans le constructeur, après this.scrollFriction = 0.95;
|
|
385
386
|
this.scrollFriction = 0.95;
|
|
386
387
|
|
|
@@ -613,23 +614,14 @@ class CanvasFramework {
|
|
|
613
614
|
// ✅ AJOUTER: Mesurer le temps d'init
|
|
614
615
|
const initTime = performance.now() - startTime;
|
|
615
616
|
|
|
616
|
-
// ✅
|
|
617
|
-
this.metrics =
|
|
618
|
-
initTime: initTime,
|
|
619
|
-
firstRenderTime: null,
|
|
620
|
-
firstInteractionTime: null,
|
|
621
|
-
totalStartupTime: null
|
|
622
|
-
};
|
|
617
|
+
// ✅ Mettre à jour les métriques (initialisées plus haut)
|
|
618
|
+
this.metrics.initTime = initTime;
|
|
623
619
|
|
|
624
620
|
// ✅ AJOUTER: Logger si debug
|
|
625
621
|
if (options.debug || options.showMetrics) {
|
|
626
622
|
console.log(`⚡ Framework initialisé en ${initTime.toFixed(2)}ms`);
|
|
627
623
|
}
|
|
628
624
|
|
|
629
|
-
// ✅ AJOUTER: Marquer le premier rendu
|
|
630
|
-
this._firstRenderDone = false;
|
|
631
|
-
this._startupStartTime = startTime;
|
|
632
|
-
|
|
633
625
|
// ✅ OPTIMISATION OPTION 5: Partition spatiale pour le culling (optionnel)
|
|
634
626
|
if (this.optimizations.useSpatialPartitioning) {
|
|
635
627
|
this._initSpatialPartitioning();
|
|
@@ -980,7 +972,10 @@ class CanvasFramework {
|
|
|
980
972
|
`;
|
|
981
973
|
|
|
982
974
|
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
|
983
|
-
|
|
975
|
+
const _scrollWorkerUrl = URL.createObjectURL(blob);
|
|
976
|
+
const _scrollWorker = new Worker(_scrollWorkerUrl);
|
|
977
|
+
URL.revokeObjectURL(_scrollWorkerUrl); // Libérer la mémoire du blob
|
|
978
|
+
return _scrollWorker;
|
|
984
979
|
}
|
|
985
980
|
|
|
986
981
|
/**
|
|
@@ -1004,12 +999,9 @@ class CanvasFramework {
|
|
|
1004
999
|
this._cachedMaxScroll = payload.maxScroll;
|
|
1005
1000
|
this._maxScrollDirty = false;
|
|
1006
1001
|
|
|
1002
|
+
// ✅ OPTIMISATION : Un simple flag au lieu d'un forEach sur tous les composants
|
|
1007
1003
|
if (Math.abs(payload.scrollVelocity) > 0) {
|
|
1008
|
-
this.
|
|
1009
|
-
if (!this.isFixedComponent(comp)) {
|
|
1010
|
-
this.markComponentDirty(comp);
|
|
1011
|
-
}
|
|
1012
|
-
});
|
|
1004
|
+
this._needsFullRender = true;
|
|
1013
1005
|
}
|
|
1014
1006
|
|
|
1015
1007
|
// ✅ AJOUTER: Continuer l'animation de retour si nécessaire
|
|
@@ -1197,6 +1189,13 @@ class CanvasFramework {
|
|
|
1197
1189
|
const key = `${text}_${font}_${color}`;
|
|
1198
1190
|
|
|
1199
1191
|
if (!this.textCache.has(key)) {
|
|
1192
|
+
// ✅ Limite LRU : éviter une croissance mémoire infinie
|
|
1193
|
+
if (this.textCache.size >= 500) {
|
|
1194
|
+
// Supprimer la première entrée (la plus ancienne)
|
|
1195
|
+
const firstKey = this.textCache.keys().next().value;
|
|
1196
|
+
this.textCache.delete(firstKey);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1200
1199
|
// Rendu dans un canvas temporaire
|
|
1201
1200
|
const temp = document.createElement('canvas');
|
|
1202
1201
|
const tempCtx = temp.getContext('2d', {
|
|
@@ -1448,6 +1447,7 @@ class CanvasFramework {
|
|
|
1448
1447
|
}
|
|
1449
1448
|
|
|
1450
1449
|
// Marquer tous les composants comme sales pour forcer un redessin complet
|
|
1450
|
+
this._needsFullRender = true;
|
|
1451
1451
|
this.components.forEach(comp => this.markComponentDirty(comp));
|
|
1452
1452
|
}
|
|
1453
1453
|
}
|
|
@@ -1477,6 +1477,14 @@ class CanvasFramework {
|
|
|
1477
1477
|
logoImage.src = opts.logo;
|
|
1478
1478
|
}
|
|
1479
1479
|
|
|
1480
|
+
// ✅ OPTIMISATION : Créer le gradient UNE seule fois avant la boucle
|
|
1481
|
+
let splashGradient = null;
|
|
1482
|
+
if (Array.isArray(opts.backgroundColor) && opts.backgroundColor.length >= 2) {
|
|
1483
|
+
splashGradient = this.ctx.createLinearGradient(0, 0, this.width, this.height);
|
|
1484
|
+
splashGradient.addColorStop(0, opts.backgroundColor[0]);
|
|
1485
|
+
splashGradient.addColorStop(1, opts.backgroundColor[1]);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1480
1488
|
const animate = () => {
|
|
1481
1489
|
const elapsed = performance.now() - startTime;
|
|
1482
1490
|
const progress = Math.min(elapsed / opts.duration, 1);
|
|
@@ -1484,17 +1492,8 @@ class CanvasFramework {
|
|
|
1484
1492
|
// Clear
|
|
1485
1493
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
1486
1494
|
|
|
1487
|
-
// ✅ Background (gradient ou couleur unie)
|
|
1488
|
-
|
|
1489
|
-
// Gradient
|
|
1490
|
-
const gradient = this.ctx.createLinearGradient(0, 0, this.width, this.height);
|
|
1491
|
-
gradient.addColorStop(0, opts.backgroundColor[0]);
|
|
1492
|
-
gradient.addColorStop(1, opts.backgroundColor[1]);
|
|
1493
|
-
this.ctx.fillStyle = gradient;
|
|
1494
|
-
} else {
|
|
1495
|
-
// Couleur unie
|
|
1496
|
-
this.ctx.fillStyle = opts.backgroundColor;
|
|
1497
|
-
}
|
|
1495
|
+
// ✅ Background (gradient pré-calculé ou couleur unie)
|
|
1496
|
+
this.ctx.fillStyle = splashGradient || opts.backgroundColor;
|
|
1498
1497
|
this.ctx.fillRect(0, 0, this.width, this.height);
|
|
1499
1498
|
|
|
1500
1499
|
const centerX = this.width / 2;
|
|
@@ -1568,6 +1567,14 @@ class CanvasFramework {
|
|
|
1568
1567
|
const duration = opts.fadeOutDuration;
|
|
1569
1568
|
const startTime = performance.now();
|
|
1570
1569
|
|
|
1570
|
+
// ✅ OPTIMISATION : Créer le gradient UNE seule fois avant la boucle
|
|
1571
|
+
let fadeGradient = null;
|
|
1572
|
+
if (Array.isArray(opts.backgroundColor) && opts.backgroundColor.length >= 2) {
|
|
1573
|
+
fadeGradient = this.ctx.createLinearGradient(0, 0, this.width, this.height);
|
|
1574
|
+
fadeGradient.addColorStop(0, opts.backgroundColor[0]);
|
|
1575
|
+
fadeGradient.addColorStop(1, opts.backgroundColor[1]);
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1571
1578
|
const fade = () => {
|
|
1572
1579
|
const elapsed = performance.now() - startTime;
|
|
1573
1580
|
const progress = elapsed / duration;
|
|
@@ -1577,15 +1584,8 @@ class CanvasFramework {
|
|
|
1577
1584
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
1578
1585
|
this.ctx.globalAlpha = alpha;
|
|
1579
1586
|
|
|
1580
|
-
// Redessiner le background
|
|
1581
|
-
|
|
1582
|
-
const gradient = this.ctx.createLinearGradient(0, 0, this.width, this.height);
|
|
1583
|
-
gradient.addColorStop(0, opts.backgroundColor[0]);
|
|
1584
|
-
gradient.addColorStop(1, opts.backgroundColor[1]);
|
|
1585
|
-
this.ctx.fillStyle = gradient;
|
|
1586
|
-
} else {
|
|
1587
|
-
this.ctx.fillStyle = opts.backgroundColor;
|
|
1588
|
-
}
|
|
1587
|
+
// Redessiner le background avec le gradient pré-calculé
|
|
1588
|
+
this.ctx.fillStyle = fadeGradient || opts.backgroundColor;
|
|
1589
1589
|
this.ctx.fillRect(0, 0, this.width, this.height);
|
|
1590
1590
|
|
|
1591
1591
|
// Spinner pendant le fade
|
|
@@ -1603,17 +1603,18 @@ class CanvasFramework {
|
|
|
1603
1603
|
requestAnimationFrame(fade);
|
|
1604
1604
|
} else {
|
|
1605
1605
|
this._splashFinished = true;
|
|
1606
|
-
// ✅
|
|
1606
|
+
// ✅ Réinitialiser complètement le contexte
|
|
1607
1607
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
1608
1608
|
this.ctx.globalAlpha = 1;
|
|
1609
|
-
this.ctx.textAlign = 'start';
|
|
1610
|
-
this.ctx.textBaseline = 'alphabetic';
|
|
1611
|
-
this.ctx.font = '10px sans-serif';
|
|
1609
|
+
this.ctx.textAlign = 'start';
|
|
1610
|
+
this.ctx.textBaseline = 'alphabetic';
|
|
1611
|
+
this.ctx.font = '10px sans-serif';
|
|
1612
1612
|
this.ctx.fillStyle = '#000000';
|
|
1613
1613
|
this.ctx.strokeStyle = '#000000';
|
|
1614
1614
|
this.ctx.lineWidth = 1;
|
|
1615
1615
|
this.ctx.lineCap = 'butt';
|
|
1616
1616
|
this.ctx.lineJoin = 'miter';
|
|
1617
|
+
this._needsFullRender = true;
|
|
1617
1618
|
}
|
|
1618
1619
|
};
|
|
1619
1620
|
|
|
@@ -1773,19 +1774,14 @@ class CanvasFramework {
|
|
|
1773
1774
|
}
|
|
1774
1775
|
}
|
|
1775
1776
|
|
|
1777
|
+
/**
|
|
1778
|
+
* Méthode réservée pour les futures personnalisations de contexte liées au thème.
|
|
1779
|
+
* L'ancienne implémentation modifiait Object.defineProperty sur le prototype global,
|
|
1780
|
+
* ce qui pouvait causer des effets de bord. Désactivée intentionnellement.
|
|
1781
|
+
*/
|
|
1776
1782
|
wrapContext(ctx, theme) {
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
set: (value) => {
|
|
1780
|
-
// Si value est blanc/noir ou une couleur “neutre”, tu remplaces par theme
|
|
1781
|
-
if (value === '#FFFFFF' || value === '#000000') {
|
|
1782
|
-
originalFillStyle.set.call(ctx, value);
|
|
1783
|
-
} else {
|
|
1784
|
-
originalFillStyle.set.call(ctx, value);
|
|
1785
|
-
}
|
|
1786
|
-
},
|
|
1787
|
-
get: () => originalFillStyle.get.call(ctx)
|
|
1788
|
-
});
|
|
1783
|
+
// Intentionnellement vide : les deux branches produisaient le même effet.
|
|
1784
|
+
// À ré-implémenter proprement si un remplacement de couleur basé sur le thème est requis.
|
|
1789
1785
|
}
|
|
1790
1786
|
|
|
1791
1787
|
createCanvasWorker() {
|
|
@@ -1840,9 +1836,10 @@ class CanvasFramework {
|
|
|
1840
1836
|
|
|
1841
1837
|
// Protège la boucle
|
|
1842
1838
|
if (this.components && Array.isArray(this.components)) {
|
|
1839
|
+
this._needsFullRender = true;
|
|
1843
1840
|
this.components.forEach(comp => comp.markDirty());
|
|
1844
1841
|
} else {
|
|
1845
|
-
console.warn('[setTheme] components pas encore initialisé');
|
|
1842
|
+
if (this.debbug) console.warn('[setTheme] components pas encore initialisé');
|
|
1846
1843
|
}
|
|
1847
1844
|
}
|
|
1848
1845
|
|
|
@@ -1957,13 +1954,22 @@ class CanvasFramework {
|
|
|
1957
1954
|
}
|
|
1958
1955
|
|
|
1959
1956
|
setupEventListeners() {
|
|
1960
|
-
|
|
1961
|
-
this.
|
|
1962
|
-
this.
|
|
1963
|
-
this.
|
|
1964
|
-
this.
|
|
1965
|
-
this.
|
|
1966
|
-
|
|
1957
|
+
// Sauvegarder les références bound pour pouvoir les retirer dans destroy()
|
|
1958
|
+
this._boundHandleTouchStart = this.handleTouchStart.bind(this);
|
|
1959
|
+
this._boundHandleTouchMove = this.handleTouchMove.bind(this);
|
|
1960
|
+
this._boundHandleTouchEnd = this.handleTouchEnd.bind(this);
|
|
1961
|
+
this._boundHandleMouseDown = this.handleMouseDown.bind(this);
|
|
1962
|
+
this._boundHandleMouseMove = this.handleMouseMove.bind(this);
|
|
1963
|
+
this._boundHandleMouseUp = this.handleMouseUp.bind(this);
|
|
1964
|
+
this._boundHandleResize = this.handleResize.bind(this);
|
|
1965
|
+
|
|
1966
|
+
this.canvas.addEventListener('touchstart', this._boundHandleTouchStart, { passive: false });
|
|
1967
|
+
this.canvas.addEventListener('touchmove', this._boundHandleTouchMove, { passive: false });
|
|
1968
|
+
this.canvas.addEventListener('touchend', this._boundHandleTouchEnd, { passive: false });
|
|
1969
|
+
this.canvas.addEventListener('mousedown', this._boundHandleMouseDown);
|
|
1970
|
+
this.canvas.addEventListener('mousemove', this._boundHandleMouseMove);
|
|
1971
|
+
this.canvas.addEventListener('mouseup', this._boundHandleMouseUp);
|
|
1972
|
+
window.addEventListener('resize', this._boundHandleResize);
|
|
1967
1973
|
}
|
|
1968
1974
|
|
|
1969
1975
|
/**
|
|
@@ -2250,15 +2256,7 @@ class CanvasFramework {
|
|
|
2250
2256
|
}
|
|
2251
2257
|
|
|
2252
2258
|
this._maxScrollDirty = true;
|
|
2253
|
-
|
|
2254
|
-
// Historique navigateur : seulement si ce n'est pas un retour interne
|
|
2255
|
-
if (!options._internal) {
|
|
2256
|
-
if (!replace) {
|
|
2257
|
-
window.history.pushState({ route: path, _app: true }, '', path);
|
|
2258
|
-
} else {
|
|
2259
|
-
window.history.replaceState({ route: path, _app: true }, '', path);
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2259
|
+
this._needsFullRender = true; // Forcer un rendu complet après navigation
|
|
2262
2260
|
}
|
|
2263
2261
|
|
|
2264
2262
|
// Alias pour usage interne (sans toucher window.history)
|
|
@@ -2754,16 +2752,6 @@ class CanvasFramework {
|
|
|
2754
2752
|
return Math.max(0, maxY - this.height + 50);
|
|
2755
2753
|
}
|
|
2756
2754
|
|
|
2757
|
-
/*getMaxScroll() {
|
|
2758
|
-
let maxY = 0;
|
|
2759
|
-
for (let comp of this.components) {
|
|
2760
|
-
if (!this.isFixedComponent(comp)) {
|
|
2761
|
-
maxY = Math.max(maxY, comp.y + comp.height);
|
|
2762
|
-
}
|
|
2763
|
-
}
|
|
2764
|
-
return Math.max(0, maxY - this.height + 50);
|
|
2765
|
-
}*/
|
|
2766
|
-
|
|
2767
2755
|
handleResize() {
|
|
2768
2756
|
if (this.resizeTimeout) clearTimeout(this.resizeTimeout);
|
|
2769
2757
|
|
|
@@ -2774,18 +2762,19 @@ class CanvasFramework {
|
|
|
2774
2762
|
// Règle clé : on resize UNIQUEMENT si la largeur a vraiment changé
|
|
2775
2763
|
// (rotation, split-screen, vraie fenêtre changée)
|
|
2776
2764
|
// À la fermeture du clavier → largeur = identique → on skip
|
|
2777
|
-
if (Math.abs(newWidth - this.width) <= 8) {
|
|
2778
|
-
console.log("[resize] Largeur identique → probablement clavier (open/close) → ignoré");
|
|
2765
|
+
if (Math.abs(newWidth - this.width) <= 8) {
|
|
2766
|
+
if (this.debbug) console.log("[resize] Largeur identique → probablement clavier (open/close) → ignoré");
|
|
2779
2767
|
return;
|
|
2780
2768
|
}
|
|
2781
2769
|
|
|
2782
|
-
console.log("[resize] Largeur changée → vrai resize");
|
|
2770
|
+
if (this.debbug) console.log("[resize] Largeur changée → vrai resize");
|
|
2783
2771
|
|
|
2784
2772
|
this.width = newWidth;
|
|
2785
2773
|
this.height = newHeight;
|
|
2786
2774
|
|
|
2787
2775
|
this.setupCanvas(); // ← change canvas.width/height + DPR + style
|
|
2788
2776
|
this._maxScrollDirty = true;
|
|
2777
|
+
this._needsFullRender = true; // Forcer un rendu complet après resize
|
|
2789
2778
|
|
|
2790
2779
|
if (this.scrollWorker) {
|
|
2791
2780
|
this.scrollWorker.postMessage({
|
|
@@ -2822,6 +2811,7 @@ class CanvasFramework {
|
|
|
2822
2811
|
this.components.push(component);
|
|
2823
2812
|
component._mount();
|
|
2824
2813
|
this.updateScrollWorkerComponents();
|
|
2814
|
+
this._needsFullRender = true; // Forcer un rendu complet après ajout
|
|
2825
2815
|
return component;
|
|
2826
2816
|
}
|
|
2827
2817
|
|
|
@@ -2831,6 +2821,7 @@ class CanvasFramework {
|
|
|
2831
2821
|
component._unmount();
|
|
2832
2822
|
this.components.splice(index, 1);
|
|
2833
2823
|
this.updateScrollWorkerComponents();
|
|
2824
|
+
this._needsFullRender = true; // Forcer un rendu complet après suppression
|
|
2834
2825
|
}
|
|
2835
2826
|
}
|
|
2836
2827
|
|
|
@@ -2839,10 +2830,12 @@ class CanvasFramework {
|
|
|
2839
2830
|
// Vérifications basiques
|
|
2840
2831
|
if (!component || !component.visible) return;
|
|
2841
2832
|
|
|
2842
|
-
// ✅ TOUJOURS ajouter au set des composants sales
|
|
2843
|
-
// Les conditions de rendu seront vérifiées dans _renderDirtyComponents
|
|
2844
2833
|
if (this.optimizationEnabled) {
|
|
2834
|
+
// ✅ Ajouter au set des composants sales pour rendu partiel
|
|
2845
2835
|
this.dirtyComponents.add(component);
|
|
2836
|
+
} else {
|
|
2837
|
+
// Sans optimisation : forcer un rendu complet
|
|
2838
|
+
this._needsFullRender = true;
|
|
2846
2839
|
}
|
|
2847
2840
|
|
|
2848
2841
|
// ✅ Optionnel : Marquer aussi le composant lui-même
|
|
@@ -3016,8 +3009,7 @@ class CanvasFramework {
|
|
|
3016
3009
|
}
|
|
3017
3010
|
|
|
3018
3011
|
startRenderLoop() {
|
|
3019
|
-
|
|
3020
|
-
this._needsRender = true; // ← AJOUTER
|
|
3012
|
+
this._needsRender = true;
|
|
3021
3013
|
|
|
3022
3014
|
const render = () => {
|
|
3023
3015
|
if (!this._splashFinished) {
|
|
@@ -3031,25 +3023,22 @@ class CanvasFramework {
|
|
|
3031
3023
|
|
|
3032
3024
|
// Si une transition est en cours
|
|
3033
3025
|
if (this.transitionState.isTransitioning) {
|
|
3034
|
-
// Mettre à jour la progression
|
|
3035
3026
|
const elapsed = Date.now() - this.transitionState.startTime;
|
|
3036
3027
|
this.transitionState.progress = Math.min(elapsed / this.transitionState.duration, 1);
|
|
3037
3028
|
|
|
3038
|
-
// Rendu spécial pour la transition
|
|
3039
3029
|
this.renderSimpleTransition();
|
|
3040
3030
|
|
|
3041
|
-
// Si la transition est terminée
|
|
3042
3031
|
if (this.transitionState.progress >= 1) {
|
|
3043
3032
|
this.transitionState.isTransitioning = false;
|
|
3044
3033
|
this.transitionState.oldComponents = [];
|
|
3045
3034
|
}
|
|
3046
|
-
}
|
|
3047
|
-
//
|
|
3035
|
+
}
|
|
3036
|
+
// Rendu normal — toujours dessiner pour garantir l'affichage
|
|
3048
3037
|
else {
|
|
3049
3038
|
this.renderFull();
|
|
3050
3039
|
}
|
|
3051
3040
|
|
|
3052
|
-
// Calcul FPS
|
|
3041
|
+
// Calcul FPS
|
|
3053
3042
|
this._frames++;
|
|
3054
3043
|
const now = performance.now();
|
|
3055
3044
|
const elapsed = now - this._lastFpsTime;
|
|
@@ -3226,26 +3215,6 @@ class CanvasFramework {
|
|
|
3226
3215
|
ctx.restore();
|
|
3227
3216
|
}
|
|
3228
3217
|
|
|
3229
|
-
/**
|
|
3230
|
-
* Mettre à jour la progression de la transition
|
|
3231
|
-
* @private
|
|
3232
|
-
*/
|
|
3233
|
-
updateTransition() {
|
|
3234
|
-
if (!this.transitionState.isTransitioning) return;
|
|
3235
|
-
|
|
3236
|
-
const elapsed = Date.now() - this.transitionState.startTime;
|
|
3237
|
-
this.transitionState.progress = Math.min(elapsed / this.transitionState.duration, 1);
|
|
3238
|
-
|
|
3239
|
-
// Si la transition est terminée
|
|
3240
|
-
if (this.transitionState.progress >= 1) {
|
|
3241
|
-
this.transitionState.isTransitioning = false;
|
|
3242
|
-
|
|
3243
|
-
// Marquer tous les nouveaux composants comme sales pour le prochain rendu
|
|
3244
|
-
this.transitionState.newComponents.forEach(comp => {
|
|
3245
|
-
this.markComponentDirty(comp);
|
|
3246
|
-
});
|
|
3247
|
-
}
|
|
3248
|
-
}
|
|
3249
3218
|
|
|
3250
3219
|
/**
|
|
3251
3220
|
* Rendu complet normal (sans transition)
|
|
@@ -3368,14 +3337,18 @@ class CanvasFramework {
|
|
|
3368
3337
|
this.ctx.destroy();
|
|
3369
3338
|
}
|
|
3370
3339
|
|
|
3371
|
-
// Nettoyer les écouteurs d'événements
|
|
3372
|
-
this.canvas.removeEventListener('touchstart', this.
|
|
3373
|
-
this.canvas.removeEventListener('touchmove',
|
|
3374
|
-
this.canvas.removeEventListener('touchend',
|
|
3375
|
-
this.canvas.removeEventListener('mousedown',
|
|
3376
|
-
this.canvas.removeEventListener('mousemove',
|
|
3377
|
-
this.canvas.removeEventListener('mouseup',
|
|
3378
|
-
window.removeEventListener('resize',
|
|
3340
|
+
// Nettoyer les écouteurs d'événements (références bound sauvegardées)
|
|
3341
|
+
this.canvas.removeEventListener('touchstart', this._boundHandleTouchStart);
|
|
3342
|
+
this.canvas.removeEventListener('touchmove', this._boundHandleTouchMove);
|
|
3343
|
+
this.canvas.removeEventListener('touchend', this._boundHandleTouchEnd);
|
|
3344
|
+
this.canvas.removeEventListener('mousedown', this._boundHandleMouseDown);
|
|
3345
|
+
this.canvas.removeEventListener('mousemove', this._boundHandleMouseMove);
|
|
3346
|
+
this.canvas.removeEventListener('mouseup', this._boundHandleMouseUp);
|
|
3347
|
+
window.removeEventListener('resize', this._boundHandleResize);
|
|
3348
|
+
|
|
3349
|
+
// Vider le textCache pour libérer la mémoire
|
|
3350
|
+
if (this.textCache) this.textCache.clear();
|
|
3351
|
+
if (this.imageCache) this.imageCache.clear();
|
|
3379
3352
|
}
|
|
3380
3353
|
|
|
3381
3354
|
// ✅ AJOUTER: Afficher les métriques à l'écran
|