canvasframework 0.6.0 → 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/components/Camera.js +315 -368
- package/components/FloatedCamera.js +4 -5
- package/core/CanvasFramework.js +152 -122
- package/core/WebGLCanvasAdapter.js +640 -641
- package/package.json +1 -1
|
@@ -572,11 +572,10 @@ class FloatedCamera extends Component {
|
|
|
572
572
|
|
|
573
573
|
// 2. Mode contain/cover (DROITE)
|
|
574
574
|
const mb = this._getModeBtnBounds();
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
ctx.strokeRect(this.x + mb.x, this.y + mb.y, mb.size, mb.size);
|
|
575
|
+
ctx.fillStyle = 'rgba(255,255,255,0.9)';
|
|
576
|
+
ctx.beginPath();
|
|
577
|
+
ctx.arc(this.x + mb.x + mb.size / 2, this.y + mb.y + mb.size / 2, mb.size / 2, 0, Math.PI * 2);
|
|
578
|
+
ctx.fill();
|
|
580
579
|
if (this.fitMode === 'contain') {
|
|
581
580
|
this.drawContainIcon(ctx, this.x + mb.x, this.y + mb.y, mb.size);
|
|
582
581
|
} else {
|
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
|
|
|
@@ -585,6 +586,7 @@ class CanvasFramework {
|
|
|
585
586
|
|
|
586
587
|
this.setupEventListeners();
|
|
587
588
|
this.setupHistoryListener();
|
|
589
|
+
this._initNavigationGuard(); // ← AJOUTER
|
|
588
590
|
|
|
589
591
|
this.startRenderLoop();
|
|
590
592
|
|
|
@@ -612,29 +614,27 @@ class CanvasFramework {
|
|
|
612
614
|
// ✅ AJOUTER: Mesurer le temps d'init
|
|
613
615
|
const initTime = performance.now() - startTime;
|
|
614
616
|
|
|
615
|
-
// ✅
|
|
616
|
-
this.metrics =
|
|
617
|
-
initTime: initTime,
|
|
618
|
-
firstRenderTime: null,
|
|
619
|
-
firstInteractionTime: null,
|
|
620
|
-
totalStartupTime: null
|
|
621
|
-
};
|
|
617
|
+
// ✅ Mettre à jour les métriques (initialisées plus haut)
|
|
618
|
+
this.metrics.initTime = initTime;
|
|
622
619
|
|
|
623
620
|
// ✅ AJOUTER: Logger si debug
|
|
624
621
|
if (options.debug || options.showMetrics) {
|
|
625
622
|
console.log(`⚡ Framework initialisé en ${initTime.toFixed(2)}ms`);
|
|
626
623
|
}
|
|
627
624
|
|
|
628
|
-
// ✅ AJOUTER: Marquer le premier rendu
|
|
629
|
-
this._firstRenderDone = false;
|
|
630
|
-
this._startupStartTime = startTime;
|
|
631
|
-
|
|
632
625
|
// ✅ OPTIMISATION OPTION 5: Partition spatiale pour le culling (optionnel)
|
|
633
626
|
if (this.optimizations.useSpatialPartitioning) {
|
|
634
627
|
this._initSpatialPartitioning();
|
|
635
628
|
}
|
|
636
629
|
|
|
637
630
|
}
|
|
631
|
+
|
|
632
|
+
_initNavigationGuard() {
|
|
633
|
+
// Injecter 2 entrées : une base + la route actuelle
|
|
634
|
+
// Ainsi le navigateur a toujours une entrée "avant" à consommer
|
|
635
|
+
window.history.replaceState({ route: '/', _guard: true }, '', '/');
|
|
636
|
+
window.history.pushState({ route: '/', _app: true }, '', '/');
|
|
637
|
+
}
|
|
638
638
|
|
|
639
639
|
/**
|
|
640
640
|
* Crée un élément DOM temporaire, l'ajoute au body, exécute une callback, puis le supprime
|
|
@@ -972,7 +972,10 @@ class CanvasFramework {
|
|
|
972
972
|
`;
|
|
973
973
|
|
|
974
974
|
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
|
975
|
-
|
|
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;
|
|
976
979
|
}
|
|
977
980
|
|
|
978
981
|
/**
|
|
@@ -996,12 +999,9 @@ class CanvasFramework {
|
|
|
996
999
|
this._cachedMaxScroll = payload.maxScroll;
|
|
997
1000
|
this._maxScrollDirty = false;
|
|
998
1001
|
|
|
1002
|
+
// ✅ OPTIMISATION : Un simple flag au lieu d'un forEach sur tous les composants
|
|
999
1003
|
if (Math.abs(payload.scrollVelocity) > 0) {
|
|
1000
|
-
this.
|
|
1001
|
-
if (!this.isFixedComponent(comp)) {
|
|
1002
|
-
this.markComponentDirty(comp);
|
|
1003
|
-
}
|
|
1004
|
-
});
|
|
1004
|
+
this._needsFullRender = true;
|
|
1005
1005
|
}
|
|
1006
1006
|
|
|
1007
1007
|
// ✅ AJOUTER: Continuer l'animation de retour si nécessaire
|
|
@@ -1189,6 +1189,13 @@ class CanvasFramework {
|
|
|
1189
1189
|
const key = `${text}_${font}_${color}`;
|
|
1190
1190
|
|
|
1191
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
|
+
|
|
1192
1199
|
// Rendu dans un canvas temporaire
|
|
1193
1200
|
const temp = document.createElement('canvas');
|
|
1194
1201
|
const tempCtx = temp.getContext('2d', {
|
|
@@ -1440,6 +1447,7 @@ class CanvasFramework {
|
|
|
1440
1447
|
}
|
|
1441
1448
|
|
|
1442
1449
|
// Marquer tous les composants comme sales pour forcer un redessin complet
|
|
1450
|
+
this._needsFullRender = true;
|
|
1443
1451
|
this.components.forEach(comp => this.markComponentDirty(comp));
|
|
1444
1452
|
}
|
|
1445
1453
|
}
|
|
@@ -1469,6 +1477,14 @@ class CanvasFramework {
|
|
|
1469
1477
|
logoImage.src = opts.logo;
|
|
1470
1478
|
}
|
|
1471
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
|
+
|
|
1472
1488
|
const animate = () => {
|
|
1473
1489
|
const elapsed = performance.now() - startTime;
|
|
1474
1490
|
const progress = Math.min(elapsed / opts.duration, 1);
|
|
@@ -1476,17 +1492,8 @@ class CanvasFramework {
|
|
|
1476
1492
|
// Clear
|
|
1477
1493
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
1478
1494
|
|
|
1479
|
-
// ✅ Background (gradient ou couleur unie)
|
|
1480
|
-
|
|
1481
|
-
// Gradient
|
|
1482
|
-
const gradient = this.ctx.createLinearGradient(0, 0, this.width, this.height);
|
|
1483
|
-
gradient.addColorStop(0, opts.backgroundColor[0]);
|
|
1484
|
-
gradient.addColorStop(1, opts.backgroundColor[1]);
|
|
1485
|
-
this.ctx.fillStyle = gradient;
|
|
1486
|
-
} else {
|
|
1487
|
-
// Couleur unie
|
|
1488
|
-
this.ctx.fillStyle = opts.backgroundColor;
|
|
1489
|
-
}
|
|
1495
|
+
// ✅ Background (gradient pré-calculé ou couleur unie)
|
|
1496
|
+
this.ctx.fillStyle = splashGradient || opts.backgroundColor;
|
|
1490
1497
|
this.ctx.fillRect(0, 0, this.width, this.height);
|
|
1491
1498
|
|
|
1492
1499
|
const centerX = this.width / 2;
|
|
@@ -1560,6 +1567,14 @@ class CanvasFramework {
|
|
|
1560
1567
|
const duration = opts.fadeOutDuration;
|
|
1561
1568
|
const startTime = performance.now();
|
|
1562
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
|
+
|
|
1563
1578
|
const fade = () => {
|
|
1564
1579
|
const elapsed = performance.now() - startTime;
|
|
1565
1580
|
const progress = elapsed / duration;
|
|
@@ -1569,15 +1584,8 @@ class CanvasFramework {
|
|
|
1569
1584
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
1570
1585
|
this.ctx.globalAlpha = alpha;
|
|
1571
1586
|
|
|
1572
|
-
// Redessiner le background
|
|
1573
|
-
|
|
1574
|
-
const gradient = this.ctx.createLinearGradient(0, 0, this.width, this.height);
|
|
1575
|
-
gradient.addColorStop(0, opts.backgroundColor[0]);
|
|
1576
|
-
gradient.addColorStop(1, opts.backgroundColor[1]);
|
|
1577
|
-
this.ctx.fillStyle = gradient;
|
|
1578
|
-
} else {
|
|
1579
|
-
this.ctx.fillStyle = opts.backgroundColor;
|
|
1580
|
-
}
|
|
1587
|
+
// Redessiner le background avec le gradient pré-calculé
|
|
1588
|
+
this.ctx.fillStyle = fadeGradient || opts.backgroundColor;
|
|
1581
1589
|
this.ctx.fillRect(0, 0, this.width, this.height);
|
|
1582
1590
|
|
|
1583
1591
|
// Spinner pendant le fade
|
|
@@ -1595,17 +1603,18 @@ class CanvasFramework {
|
|
|
1595
1603
|
requestAnimationFrame(fade);
|
|
1596
1604
|
} else {
|
|
1597
1605
|
this._splashFinished = true;
|
|
1598
|
-
// ✅
|
|
1606
|
+
// ✅ Réinitialiser complètement le contexte
|
|
1599
1607
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
1600
1608
|
this.ctx.globalAlpha = 1;
|
|
1601
|
-
this.ctx.textAlign = 'start';
|
|
1602
|
-
this.ctx.textBaseline = 'alphabetic';
|
|
1603
|
-
this.ctx.font = '10px sans-serif';
|
|
1609
|
+
this.ctx.textAlign = 'start';
|
|
1610
|
+
this.ctx.textBaseline = 'alphabetic';
|
|
1611
|
+
this.ctx.font = '10px sans-serif';
|
|
1604
1612
|
this.ctx.fillStyle = '#000000';
|
|
1605
1613
|
this.ctx.strokeStyle = '#000000';
|
|
1606
1614
|
this.ctx.lineWidth = 1;
|
|
1607
1615
|
this.ctx.lineCap = 'butt';
|
|
1608
1616
|
this.ctx.lineJoin = 'miter';
|
|
1617
|
+
this._needsFullRender = true;
|
|
1609
1618
|
}
|
|
1610
1619
|
};
|
|
1611
1620
|
|
|
@@ -1765,19 +1774,14 @@ class CanvasFramework {
|
|
|
1765
1774
|
}
|
|
1766
1775
|
}
|
|
1767
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
|
+
*/
|
|
1768
1782
|
wrapContext(ctx, theme) {
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
set: (value) => {
|
|
1772
|
-
// Si value est blanc/noir ou une couleur “neutre”, tu remplaces par theme
|
|
1773
|
-
if (value === '#FFFFFF' || value === '#000000') {
|
|
1774
|
-
originalFillStyle.set.call(ctx, value);
|
|
1775
|
-
} else {
|
|
1776
|
-
originalFillStyle.set.call(ctx, value);
|
|
1777
|
-
}
|
|
1778
|
-
},
|
|
1779
|
-
get: () => originalFillStyle.get.call(ctx)
|
|
1780
|
-
});
|
|
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.
|
|
1781
1785
|
}
|
|
1782
1786
|
|
|
1783
1787
|
createCanvasWorker() {
|
|
@@ -1832,9 +1836,10 @@ class CanvasFramework {
|
|
|
1832
1836
|
|
|
1833
1837
|
// Protège la boucle
|
|
1834
1838
|
if (this.components && Array.isArray(this.components)) {
|
|
1839
|
+
this._needsFullRender = true;
|
|
1835
1840
|
this.components.forEach(comp => comp.markDirty());
|
|
1836
1841
|
} else {
|
|
1837
|
-
console.warn('[setTheme] components pas encore initialisé');
|
|
1842
|
+
if (this.debbug) console.warn('[setTheme] components pas encore initialisé');
|
|
1838
1843
|
}
|
|
1839
1844
|
}
|
|
1840
1845
|
|
|
@@ -1949,13 +1954,22 @@ class CanvasFramework {
|
|
|
1949
1954
|
}
|
|
1950
1955
|
|
|
1951
1956
|
setupEventListeners() {
|
|
1952
|
-
|
|
1953
|
-
this.
|
|
1954
|
-
this.
|
|
1955
|
-
this.
|
|
1956
|
-
this.
|
|
1957
|
-
this.
|
|
1958
|
-
|
|
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);
|
|
1959
1973
|
}
|
|
1960
1974
|
|
|
1961
1975
|
/**
|
|
@@ -1963,16 +1977,51 @@ class CanvasFramework {
|
|
|
1963
1977
|
* @private
|
|
1964
1978
|
*/
|
|
1965
1979
|
setupHistoryListener() {
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1980
|
+
window.addEventListener('popstate', (e) => {
|
|
1981
|
+
// Cas 1 : L'app a un historique interne → naviguer en arrière dans l'app
|
|
1982
|
+
if (this.historyIndex > 0) {
|
|
1983
|
+
this.historyIndex--;
|
|
1984
|
+
const entry = this.history[this.historyIndex];
|
|
1985
|
+
|
|
1986
|
+
// Recharger la vue précédente sans toucher window.history
|
|
1987
|
+
this._navigateInternal(entry.path, {
|
|
1988
|
+
replace: true,
|
|
1989
|
+
animate: true,
|
|
1990
|
+
direction: 'back'
|
|
1991
|
+
});
|
|
1992
|
+
|
|
1993
|
+
// Réinjecter une entrée pour ne jamais vider la pile navigateur
|
|
1994
|
+
window.history.pushState({ route: entry.path, _app: true }, '', entry.path);
|
|
1995
|
+
|
|
1996
|
+
} else {
|
|
1997
|
+
// Cas 2 : Plus d'historique interne → réinjecter et ignorer
|
|
1998
|
+
// (empêche de quitter l'app)
|
|
1999
|
+
const currentPath = this.currentRoute || '/';
|
|
2000
|
+
window.history.pushState({ route: currentPath, _app: true }, '', currentPath);
|
|
2001
|
+
|
|
2002
|
+
// Optionnel : afficher un toast "Appuyez encore pour quitter"
|
|
2003
|
+
this._handleBackExitAttempt();
|
|
2004
|
+
}
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
_handleBackExitAttempt() {
|
|
2009
|
+
const now = Date.now();
|
|
2010
|
+
|
|
2011
|
+
if (this._lastBackAttempt && (now - this._lastBackAttempt) < 2000) {
|
|
2012
|
+
// Deux back rapides → vraiment quitter (sur PWA/WebView)
|
|
2013
|
+
// Sur navigateur standard ce n'est pas possible, mais sur Capacitor/Cordova oui
|
|
2014
|
+
if (window.navigator.app?.exitApp) {
|
|
2015
|
+
window.navigator.app.exitApp(); // Cordova
|
|
2016
|
+
} else if (window.Capacitor?.Plugins?.App) {
|
|
2017
|
+
window.Capacitor.Plugins.App.exitApp(); // Capacitor
|
|
2018
|
+
}
|
|
2019
|
+
this._lastBackAttempt = null;
|
|
2020
|
+
} else {
|
|
2021
|
+
this._lastBackAttempt = now;
|
|
2022
|
+
this.showToast('Appuyez encore pour quitter', 2000);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
1976
2025
|
|
|
1977
2026
|
// ===== MÉTHODES DE ROUTING =====
|
|
1978
2027
|
|
|
@@ -2207,6 +2256,12 @@ class CanvasFramework {
|
|
|
2207
2256
|
}
|
|
2208
2257
|
|
|
2209
2258
|
this._maxScrollDirty = true;
|
|
2259
|
+
this._needsFullRender = true; // Forcer un rendu complet après navigation
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// Alias pour usage interne (sans toucher window.history)
|
|
2263
|
+
_navigateInternal(path, options = {}) {
|
|
2264
|
+
return this.navigateTo(path, { ...options, _internal: true });
|
|
2210
2265
|
}
|
|
2211
2266
|
|
|
2212
2267
|
/**
|
|
@@ -2697,16 +2752,6 @@ class CanvasFramework {
|
|
|
2697
2752
|
return Math.max(0, maxY - this.height + 50);
|
|
2698
2753
|
}
|
|
2699
2754
|
|
|
2700
|
-
/*getMaxScroll() {
|
|
2701
|
-
let maxY = 0;
|
|
2702
|
-
for (let comp of this.components) {
|
|
2703
|
-
if (!this.isFixedComponent(comp)) {
|
|
2704
|
-
maxY = Math.max(maxY, comp.y + comp.height);
|
|
2705
|
-
}
|
|
2706
|
-
}
|
|
2707
|
-
return Math.max(0, maxY - this.height + 50);
|
|
2708
|
-
}*/
|
|
2709
|
-
|
|
2710
2755
|
handleResize() {
|
|
2711
2756
|
if (this.resizeTimeout) clearTimeout(this.resizeTimeout);
|
|
2712
2757
|
|
|
@@ -2717,18 +2762,19 @@ class CanvasFramework {
|
|
|
2717
2762
|
// Règle clé : on resize UNIQUEMENT si la largeur a vraiment changé
|
|
2718
2763
|
// (rotation, split-screen, vraie fenêtre changée)
|
|
2719
2764
|
// À la fermeture du clavier → largeur = identique → on skip
|
|
2720
|
-
if (Math.abs(newWidth - this.width) <= 8) {
|
|
2721
|
-
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é");
|
|
2722
2767
|
return;
|
|
2723
2768
|
}
|
|
2724
2769
|
|
|
2725
|
-
console.log("[resize] Largeur changée → vrai resize");
|
|
2770
|
+
if (this.debbug) console.log("[resize] Largeur changée → vrai resize");
|
|
2726
2771
|
|
|
2727
2772
|
this.width = newWidth;
|
|
2728
2773
|
this.height = newHeight;
|
|
2729
2774
|
|
|
2730
2775
|
this.setupCanvas(); // ← change canvas.width/height + DPR + style
|
|
2731
2776
|
this._maxScrollDirty = true;
|
|
2777
|
+
this._needsFullRender = true; // Forcer un rendu complet après resize
|
|
2732
2778
|
|
|
2733
2779
|
if (this.scrollWorker) {
|
|
2734
2780
|
this.scrollWorker.postMessage({
|
|
@@ -2765,6 +2811,7 @@ class CanvasFramework {
|
|
|
2765
2811
|
this.components.push(component);
|
|
2766
2812
|
component._mount();
|
|
2767
2813
|
this.updateScrollWorkerComponents();
|
|
2814
|
+
this._needsFullRender = true; // Forcer un rendu complet après ajout
|
|
2768
2815
|
return component;
|
|
2769
2816
|
}
|
|
2770
2817
|
|
|
@@ -2774,6 +2821,7 @@ class CanvasFramework {
|
|
|
2774
2821
|
component._unmount();
|
|
2775
2822
|
this.components.splice(index, 1);
|
|
2776
2823
|
this.updateScrollWorkerComponents();
|
|
2824
|
+
this._needsFullRender = true; // Forcer un rendu complet après suppression
|
|
2777
2825
|
}
|
|
2778
2826
|
}
|
|
2779
2827
|
|
|
@@ -2782,10 +2830,12 @@ class CanvasFramework {
|
|
|
2782
2830
|
// Vérifications basiques
|
|
2783
2831
|
if (!component || !component.visible) return;
|
|
2784
2832
|
|
|
2785
|
-
// ✅ TOUJOURS ajouter au set des composants sales
|
|
2786
|
-
// Les conditions de rendu seront vérifiées dans _renderDirtyComponents
|
|
2787
2833
|
if (this.optimizationEnabled) {
|
|
2834
|
+
// ✅ Ajouter au set des composants sales pour rendu partiel
|
|
2788
2835
|
this.dirtyComponents.add(component);
|
|
2836
|
+
} else {
|
|
2837
|
+
// Sans optimisation : forcer un rendu complet
|
|
2838
|
+
this._needsFullRender = true;
|
|
2789
2839
|
}
|
|
2790
2840
|
|
|
2791
2841
|
// ✅ Optionnel : Marquer aussi le composant lui-même
|
|
@@ -2959,8 +3009,7 @@ class CanvasFramework {
|
|
|
2959
3009
|
}
|
|
2960
3010
|
|
|
2961
3011
|
startRenderLoop() {
|
|
2962
|
-
|
|
2963
|
-
this._needsRender = true; // ← AJOUTER
|
|
3012
|
+
this._needsRender = true;
|
|
2964
3013
|
|
|
2965
3014
|
const render = () => {
|
|
2966
3015
|
if (!this._splashFinished) {
|
|
@@ -2974,25 +3023,22 @@ class CanvasFramework {
|
|
|
2974
3023
|
|
|
2975
3024
|
// Si une transition est en cours
|
|
2976
3025
|
if (this.transitionState.isTransitioning) {
|
|
2977
|
-
// Mettre à jour la progression
|
|
2978
3026
|
const elapsed = Date.now() - this.transitionState.startTime;
|
|
2979
3027
|
this.transitionState.progress = Math.min(elapsed / this.transitionState.duration, 1);
|
|
2980
3028
|
|
|
2981
|
-
// Rendu spécial pour la transition
|
|
2982
3029
|
this.renderSimpleTransition();
|
|
2983
3030
|
|
|
2984
|
-
// Si la transition est terminée
|
|
2985
3031
|
if (this.transitionState.progress >= 1) {
|
|
2986
3032
|
this.transitionState.isTransitioning = false;
|
|
2987
3033
|
this.transitionState.oldComponents = [];
|
|
2988
3034
|
}
|
|
2989
|
-
}
|
|
2990
|
-
//
|
|
3035
|
+
}
|
|
3036
|
+
// Rendu normal — toujours dessiner pour garantir l'affichage
|
|
2991
3037
|
else {
|
|
2992
3038
|
this.renderFull();
|
|
2993
3039
|
}
|
|
2994
3040
|
|
|
2995
|
-
// Calcul FPS
|
|
3041
|
+
// Calcul FPS
|
|
2996
3042
|
this._frames++;
|
|
2997
3043
|
const now = performance.now();
|
|
2998
3044
|
const elapsed = now - this._lastFpsTime;
|
|
@@ -3169,26 +3215,6 @@ class CanvasFramework {
|
|
|
3169
3215
|
ctx.restore();
|
|
3170
3216
|
}
|
|
3171
3217
|
|
|
3172
|
-
/**
|
|
3173
|
-
* Mettre à jour la progression de la transition
|
|
3174
|
-
* @private
|
|
3175
|
-
*/
|
|
3176
|
-
updateTransition() {
|
|
3177
|
-
if (!this.transitionState.isTransitioning) return;
|
|
3178
|
-
|
|
3179
|
-
const elapsed = Date.now() - this.transitionState.startTime;
|
|
3180
|
-
this.transitionState.progress = Math.min(elapsed / this.transitionState.duration, 1);
|
|
3181
|
-
|
|
3182
|
-
// Si la transition est terminée
|
|
3183
|
-
if (this.transitionState.progress >= 1) {
|
|
3184
|
-
this.transitionState.isTransitioning = false;
|
|
3185
|
-
|
|
3186
|
-
// Marquer tous les nouveaux composants comme sales pour le prochain rendu
|
|
3187
|
-
this.transitionState.newComponents.forEach(comp => {
|
|
3188
|
-
this.markComponentDirty(comp);
|
|
3189
|
-
});
|
|
3190
|
-
}
|
|
3191
|
-
}
|
|
3192
3218
|
|
|
3193
3219
|
/**
|
|
3194
3220
|
* Rendu complet normal (sans transition)
|
|
@@ -3311,14 +3337,18 @@ class CanvasFramework {
|
|
|
3311
3337
|
this.ctx.destroy();
|
|
3312
3338
|
}
|
|
3313
3339
|
|
|
3314
|
-
// Nettoyer les écouteurs d'événements
|
|
3315
|
-
this.canvas.removeEventListener('touchstart', this.
|
|
3316
|
-
this.canvas.removeEventListener('touchmove',
|
|
3317
|
-
this.canvas.removeEventListener('touchend',
|
|
3318
|
-
this.canvas.removeEventListener('mousedown',
|
|
3319
|
-
this.canvas.removeEventListener('mousemove',
|
|
3320
|
-
this.canvas.removeEventListener('mouseup',
|
|
3321
|
-
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();
|
|
3322
3352
|
}
|
|
3323
3353
|
|
|
3324
3354
|
// ✅ AJOUTER: Afficher les métriques à l'écran
|