canvasframework 0.5.10 → 0.5.11
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 +20 -0
- package/components/FloatedCamera.js +21 -1
- package/core/CanvasFramework.js +129 -183
- package/package.json +1 -1
package/components/Camera.js
CHANGED
|
@@ -34,6 +34,25 @@ class Camera extends Component {
|
|
|
34
34
|
|
|
35
35
|
this.isStarting = false;
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
static cleanupAllCameras() {
|
|
39
|
+
const allVideos = document.querySelectorAll('video');
|
|
40
|
+
allVideos.forEach(video => {
|
|
41
|
+
// Arrêter les streams
|
|
42
|
+
if (video.srcObject) {
|
|
43
|
+
const stream = video.srcObject;
|
|
44
|
+
if (stream && stream.getTracks) {
|
|
45
|
+
stream.getTracks().forEach(track => track.stop());
|
|
46
|
+
}
|
|
47
|
+
video.srcObject = null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Supprimer du DOM
|
|
51
|
+
if (video.parentNode) {
|
|
52
|
+
video.parentNode.removeChild(video);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
37
56
|
|
|
38
57
|
async _mount() {
|
|
39
58
|
super._mount?.();
|
|
@@ -120,6 +139,7 @@ class Camera extends Component {
|
|
|
120
139
|
handleMouseUp(e) {}
|
|
121
140
|
|
|
122
141
|
async startCamera() {
|
|
142
|
+
Camera.cleanupAllCameras();
|
|
123
143
|
if (this.stream) return;
|
|
124
144
|
|
|
125
145
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Component from '../core/Component.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Composant
|
|
4
|
+
* Composant Camera autonome avec gestion directe des clics/touches
|
|
5
5
|
* Modes : contain (tout visible + bandes), cover (remplit + crop), fit (centre sans crop)
|
|
6
6
|
*/
|
|
7
7
|
class FloatedCamera extends Component {
|
|
@@ -34,6 +34,25 @@ class FloatedCamera extends Component {
|
|
|
34
34
|
|
|
35
35
|
this.isStarting = false;
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
static cleanupAllCameras() {
|
|
39
|
+
const allVideos = document.querySelectorAll('video');
|
|
40
|
+
allVideos.forEach(video => {
|
|
41
|
+
// Arrêter les streams
|
|
42
|
+
if (video.srcObject) {
|
|
43
|
+
const stream = video.srcObject;
|
|
44
|
+
if (stream && stream.getTracks) {
|
|
45
|
+
stream.getTracks().forEach(track => track.stop());
|
|
46
|
+
}
|
|
47
|
+
video.srcObject = null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Supprimer du DOM
|
|
51
|
+
if (video.parentNode) {
|
|
52
|
+
video.parentNode.removeChild(video);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
37
56
|
|
|
38
57
|
async _mount() {
|
|
39
58
|
super._mount?.();
|
|
@@ -120,6 +139,7 @@ class FloatedCamera extends Component {
|
|
|
120
139
|
handleMouseUp(e) {}
|
|
121
140
|
|
|
122
141
|
async startCamera() {
|
|
142
|
+
FloatedCamera.cleanupAllCameras();
|
|
123
143
|
if (this.stream) return;
|
|
124
144
|
|
|
125
145
|
try {
|
package/core/CanvasFramework.js
CHANGED
|
@@ -1781,6 +1781,7 @@ class CanvasFramework {
|
|
|
1781
1781
|
* @private
|
|
1782
1782
|
*/
|
|
1783
1783
|
async navigateTo(path, options = {}) {
|
|
1784
|
+
|
|
1784
1785
|
const {
|
|
1785
1786
|
replace = false,
|
|
1786
1787
|
animate = true,
|
|
@@ -1803,7 +1804,7 @@ class CanvasFramework {
|
|
|
1803
1804
|
} = match;
|
|
1804
1805
|
|
|
1805
1806
|
// ===== LIFECYCLE: AVANT DE QUITTER L'ANCIENNE ROUTE =====
|
|
1806
|
-
|
|
1807
|
+
|
|
1807
1808
|
// Hook beforeLeave de la route actuelle (peut bloquer la navigation)
|
|
1808
1809
|
const currentRouteData = this.routes.get(this.currentRoute);
|
|
1809
1810
|
if (currentRouteData?.beforeLeave) {
|
|
@@ -1930,6 +1931,17 @@ class CanvasFramework {
|
|
|
1930
1931
|
|
|
1931
1932
|
// ✅ OPTIONNEL : Marquer les composants comme "dirty" pour forcer le rendu
|
|
1932
1933
|
this._maxScrollDirty = true;
|
|
1934
|
+
|
|
1935
|
+
this.components.forEach(comp => {
|
|
1936
|
+
// Vérifier si c'est un composant caméra
|
|
1937
|
+
if (comp.constructor.name === 'FloatedCamera' || comp.constructor.name === 'Camera') {
|
|
1938
|
+
// Arrêter le stream vidéo
|
|
1939
|
+
if (comp.stopCamera && typeof comp.stopCamera === 'function') {
|
|
1940
|
+
comp.stopCamera();
|
|
1941
|
+
console.log(`🎥 Caméra ${comp.constructor.name} arrêtée avant navigation`);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
});
|
|
1933
1945
|
}
|
|
1934
1946
|
|
|
1935
1947
|
/**
|
|
@@ -2662,65 +2674,38 @@ class CanvasFramework {
|
|
|
2662
2674
|
|
|
2663
2675
|
startRenderLoop() {
|
|
2664
2676
|
let lastScrollOffset = this.scrollOffset;
|
|
2665
|
-
let lastRenderMode = 'full';
|
|
2666
|
-
let lastFrameTime = performance.now();
|
|
2667
2677
|
|
|
2668
|
-
const render = (
|
|
2669
|
-
// Calculer le delta time
|
|
2670
|
-
const deltaTime = currentTime - lastFrameTime;
|
|
2671
|
-
lastFrameTime = currentTime;
|
|
2672
|
-
|
|
2678
|
+
const render = () => {
|
|
2673
2679
|
if (!this._splashFinished) {
|
|
2674
2680
|
requestAnimationFrame(render);
|
|
2675
2681
|
return;
|
|
2676
2682
|
}
|
|
2677
2683
|
|
|
2678
|
-
//
|
|
2684
|
+
// TOUJOURS effacer l'écran en premier
|
|
2685
|
+
this.ctx.fillStyle = this.backgroundColor || '#ffffff';
|
|
2686
|
+
this.ctx.fillRect(0, 0, this.width, this.height);
|
|
2687
|
+
|
|
2688
|
+
// Si une transition est en cours
|
|
2679
2689
|
if (this.transitionState.isTransitioning) {
|
|
2680
|
-
|
|
2690
|
+
// Mettre à jour la progression
|
|
2691
|
+
const elapsed = Date.now() - this.transitionState.startTime;
|
|
2692
|
+
this.transitionState.progress = Math.min(elapsed / this.transitionState.duration, 1);
|
|
2693
|
+
|
|
2694
|
+
// Rendu spécial pour la transition
|
|
2695
|
+
this.renderSimpleTransition();
|
|
2681
2696
|
|
|
2682
|
-
// Si la transition est terminée
|
|
2697
|
+
// Si la transition est terminée
|
|
2683
2698
|
if (this.transitionState.progress >= 1) {
|
|
2684
2699
|
this.transitionState.isTransitioning = false;
|
|
2685
2700
|
this.transitionState.oldComponents = [];
|
|
2686
2701
|
}
|
|
2687
|
-
}
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
const scrollChanged = Math.abs(this.scrollOffset - lastScrollOffset) > 0.1;
|
|
2691
|
-
lastScrollOffset = this.scrollOffset;
|
|
2692
|
-
|
|
2693
|
-
// 3. Décider du mode de rendu
|
|
2694
|
-
let renderMode = 'full';
|
|
2695
|
-
|
|
2696
|
-
// Conditions pour utiliser le rendu optimisé (dirty components)
|
|
2697
|
-
if (this.optimizationEnabled &&
|
|
2698
|
-
!this.transitionState.isTransitioning && // ← Pas pendant les transitions
|
|
2699
|
-
this.dirtyComponents.size > 0 &&
|
|
2700
|
-
!this.isDragging &&
|
|
2701
|
-
Math.abs(this.scrollVelocity) < 0.5 &&
|
|
2702
|
-
!scrollChanged &&
|
|
2703
|
-
this.dirtyComponents.size < 3) {
|
|
2704
|
-
renderMode = 'dirty';
|
|
2705
|
-
}
|
|
2706
|
-
|
|
2707
|
-
// 4. Exécuter le rendu
|
|
2708
|
-
if (this.transitionState.isTransitioning) {
|
|
2709
|
-
// Pendant les transitions, toujours utiliser un rendu complet spécial
|
|
2710
|
-
this.renderTransition();
|
|
2711
|
-
} else if (renderMode === 'full' || lastRenderMode !== renderMode) {
|
|
2712
|
-
// Rendu complet normal
|
|
2713
|
-
this.ctx.fillStyle = this.backgroundColor || '#ffffff';
|
|
2714
|
-
this.ctx.fillRect(0, 0, this.width, this.height);
|
|
2702
|
+
}
|
|
2703
|
+
// Sinon, rendu normal
|
|
2704
|
+
else {
|
|
2715
2705
|
this.renderFull();
|
|
2716
|
-
} else {
|
|
2717
|
-
// Rendu optimisé (seulement les composants sales)
|
|
2718
|
-
this._renderDirtyComponents();
|
|
2719
2706
|
}
|
|
2720
2707
|
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
// 5. Calculer et afficher le FPS
|
|
2708
|
+
// Calcul FPS (optionnel)
|
|
2724
2709
|
this._frames++;
|
|
2725
2710
|
const now = performance.now();
|
|
2726
2711
|
const elapsed = now - this._lastFpsTime;
|
|
@@ -2731,7 +2716,6 @@ class CanvasFramework {
|
|
|
2731
2716
|
this._lastFpsTime = now;
|
|
2732
2717
|
}
|
|
2733
2718
|
|
|
2734
|
-
// Afficher le FPS si activé
|
|
2735
2719
|
if (this.showFps) {
|
|
2736
2720
|
this.ctx.save();
|
|
2737
2721
|
this.ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
|
@@ -2744,158 +2728,124 @@ class CanvasFramework {
|
|
|
2744
2728
|
this.ctx.restore();
|
|
2745
2729
|
}
|
|
2746
2730
|
|
|
2747
|
-
//
|
|
2748
|
-
if (this.debbug) {
|
|
2749
|
-
this.drawOverflowIndicators();
|
|
2750
|
-
}
|
|
2751
|
-
|
|
2752
|
-
// Marquer le premier rendu
|
|
2753
|
-
if (!this._firstRenderDone) {
|
|
2754
|
-
this._markFirstRender();
|
|
2755
|
-
}
|
|
2756
|
-
|
|
2757
|
-
// Mettre à jour l'inertie du scroll si nécessaire
|
|
2731
|
+
// Mettre à jour l'inertie du scroll
|
|
2758
2732
|
if (Math.abs(this.scrollVelocity) > 0.1 && !this.isDragging) {
|
|
2759
2733
|
this.scrollWorker.postMessage({ type: 'UPDATE_INERTIA' });
|
|
2760
2734
|
}
|
|
2761
2735
|
|
|
2762
|
-
// Nettoyer les composants sales après le rendu
|
|
2763
|
-
if (renderMode === 'full') {
|
|
2764
|
-
this.dirtyComponents.clear();
|
|
2765
|
-
}
|
|
2766
|
-
|
|
2767
2736
|
requestAnimationFrame(render);
|
|
2768
2737
|
};
|
|
2769
2738
|
|
|
2770
|
-
render(
|
|
2739
|
+
render();
|
|
2771
2740
|
}
|
|
2772
|
-
|
|
2773
|
-
|
|
2741
|
+
|
|
2774
2742
|
/**
|
|
2775
|
-
* Rendu
|
|
2776
|
-
* @private
|
|
2743
|
+
* Rendu ultra simple pour la transition slide
|
|
2777
2744
|
*/
|
|
2778
|
-
|
|
2779
|
-
const
|
|
2745
|
+
renderSimpleTransition() {
|
|
2746
|
+
const { progress, type, direction, oldComponents, newComponents } = this.transitionState;
|
|
2780
2747
|
|
|
2781
|
-
//
|
|
2782
|
-
|
|
2783
|
-
ctx.fillRect(0, 0, this.width, this.height);
|
|
2784
|
-
|
|
2785
|
-
// Dessiner l'animation de transition
|
|
2786
|
-
const progress = this.transitionState.progress;
|
|
2787
|
-
const { type, direction, oldComponents, newComponents } = this.transitionState;
|
|
2748
|
+
// Calculer la position de décalage
|
|
2749
|
+
const slideOffset = this.width * (1 - progress);
|
|
2788
2750
|
const directionMultiplier = direction === 'forward' ? 1 : -1;
|
|
2789
2751
|
|
|
2790
|
-
//
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
comp.draw(ctx);
|
|
2812
|
-
ctx.restore();
|
|
2813
|
-
}
|
|
2814
|
-
}
|
|
2815
|
-
}
|
|
2816
|
-
ctx.restore();
|
|
2817
|
-
|
|
2818
|
-
// 2. Nouvelle vue qui entre
|
|
2819
|
-
ctx.save();
|
|
2820
|
-
ctx.translate(this.width * (1 - eased) * directionMultiplier, 0);
|
|
2821
|
-
|
|
2822
|
-
for (let comp of newComponents) {
|
|
2823
|
-
if (comp && comp.visible) {
|
|
2824
|
-
const isFixed = this.isFixedComponent(comp);
|
|
2825
|
-
|
|
2826
|
-
if (isFixed) {
|
|
2827
|
-
comp.draw(ctx);
|
|
2828
|
-
} else {
|
|
2829
|
-
ctx.save();
|
|
2830
|
-
ctx.translate(0, this.scrollOffset);
|
|
2831
|
-
comp.draw(ctx);
|
|
2832
|
-
ctx.restore();
|
|
2833
|
-
}
|
|
2834
|
-
}
|
|
2835
|
-
}
|
|
2836
|
-
ctx.restore();
|
|
2837
|
-
break;
|
|
2838
|
-
|
|
2839
|
-
case 'fade':
|
|
2840
|
-
// Ancienne vue qui disparaît
|
|
2841
|
-
ctx.save();
|
|
2842
|
-
ctx.globalAlpha = 1 - eased;
|
|
2843
|
-
for (let comp of oldComponents) {
|
|
2844
|
-
if (comp && comp.visible) {
|
|
2845
|
-
const isFixed = this.isFixedComponent(comp);
|
|
2846
|
-
|
|
2847
|
-
if (isFixed) {
|
|
2848
|
-
comp.draw(ctx);
|
|
2849
|
-
} else {
|
|
2850
|
-
ctx.save();
|
|
2851
|
-
ctx.translate(0, this.scrollOffset);
|
|
2852
|
-
comp.draw(ctx);
|
|
2853
|
-
ctx.restore();
|
|
2854
|
-
}
|
|
2855
|
-
}
|
|
2856
|
-
}
|
|
2857
|
-
ctx.restore();
|
|
2858
|
-
|
|
2859
|
-
// Nouvelle vue qui apparaît
|
|
2860
|
-
ctx.save();
|
|
2861
|
-
ctx.globalAlpha = eased;
|
|
2862
|
-
for (let comp of newComponents) {
|
|
2863
|
-
if (comp && comp.visible) {
|
|
2864
|
-
const isFixed = this.isFixedComponent(comp);
|
|
2865
|
-
|
|
2866
|
-
if (isFixed) {
|
|
2867
|
-
comp.draw(ctx);
|
|
2868
|
-
} else {
|
|
2869
|
-
ctx.save();
|
|
2870
|
-
ctx.translate(0, this.scrollOffset);
|
|
2871
|
-
comp.draw(ctx);
|
|
2872
|
-
ctx.restore();
|
|
2873
|
-
}
|
|
2752
|
+
// Pour l'animation slide, dessiner seulement la nouvelle vue à la bonne position
|
|
2753
|
+
if (type === 'slide') {
|
|
2754
|
+
// Sauvegarder le contexte
|
|
2755
|
+
this.ctx.save();
|
|
2756
|
+
|
|
2757
|
+
// Appliquer la translation pour l'animation
|
|
2758
|
+
this.ctx.translate(slideOffset * directionMultiplier, 0);
|
|
2759
|
+
|
|
2760
|
+
// Dessiner UNIQUEMENT les nouveaux composants
|
|
2761
|
+
for (let comp of newComponents) {
|
|
2762
|
+
if (comp && comp.visible) {
|
|
2763
|
+
const isFixed = this.isFixedComponent(comp);
|
|
2764
|
+
|
|
2765
|
+
if (isFixed) {
|
|
2766
|
+
comp.draw(this.ctx);
|
|
2767
|
+
} else {
|
|
2768
|
+
// Pour les composants scrollables, ajouter le décalage
|
|
2769
|
+
this.ctx.save();
|
|
2770
|
+
this.ctx.translate(0, this.scrollOffset);
|
|
2771
|
+
comp.draw(this.ctx);
|
|
2772
|
+
this.ctx.restore();
|
|
2874
2773
|
}
|
|
2875
2774
|
}
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
this.ctx.restore();
|
|
2778
|
+
}
|
|
2779
|
+
// Pour fade, dessiner la nouvelle vue avec alpha
|
|
2780
|
+
else if (type === 'fade') {
|
|
2781
|
+
this.ctx.save();
|
|
2782
|
+
this.ctx.globalAlpha = progress;
|
|
2783
|
+
|
|
2784
|
+
for (let comp of newComponents) {
|
|
2785
|
+
if (comp && comp.visible) {
|
|
2786
|
+
const isFixed = this.isFixedComponent(comp);
|
|
2787
|
+
|
|
2788
|
+
if (isFixed) {
|
|
2789
|
+
comp.draw(this.ctx);
|
|
2790
|
+
} else {
|
|
2791
|
+
this.ctx.save();
|
|
2792
|
+
this.ctx.translate(0, this.scrollOffset);
|
|
2793
|
+
comp.draw(this.ctx);
|
|
2794
|
+
this.ctx.restore();
|
|
2893
2795
|
}
|
|
2894
2796
|
}
|
|
2895
|
-
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
this.ctx.restore();
|
|
2800
|
+
}
|
|
2801
|
+
// Pour 'none', dessiner normalement
|
|
2802
|
+
else {
|
|
2803
|
+
this.renderFull();
|
|
2896
2804
|
}
|
|
2897
2805
|
}
|
|
2898
2806
|
|
|
2807
|
+
/**
|
|
2808
|
+
* Rendu normal (sans transition)
|
|
2809
|
+
*/
|
|
2810
|
+
renderFull() {
|
|
2811
|
+
this.ctx.save();
|
|
2812
|
+
|
|
2813
|
+
// Séparer les composants fixes et scrollables
|
|
2814
|
+
const scrollableComponents = [];
|
|
2815
|
+
const fixedComponents = [];
|
|
2816
|
+
|
|
2817
|
+
for (let comp of this.components) {
|
|
2818
|
+
if (this.isFixedComponent(comp)) {
|
|
2819
|
+
fixedComponents.push(comp);
|
|
2820
|
+
} else {
|
|
2821
|
+
scrollableComponents.push(comp);
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
// Dessiner les composants scrollables
|
|
2826
|
+
if (scrollableComponents.length > 0) {
|
|
2827
|
+
this.ctx.save();
|
|
2828
|
+
this.ctx.translate(0, this.scrollOffset);
|
|
2829
|
+
|
|
2830
|
+
for (let comp of scrollableComponents) {
|
|
2831
|
+
if (comp.visible) {
|
|
2832
|
+
comp.draw(this.ctx);
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
this.ctx.restore();
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
// Dessiner les composants fixes
|
|
2840
|
+
for (let comp of fixedComponents) {
|
|
2841
|
+
if (comp.visible) {
|
|
2842
|
+
comp.draw(this.ctx);
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
this.ctx.restore();
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2899
2849
|
/**
|
|
2900
2850
|
* Mettre à jour la progression de la transition
|
|
2901
2851
|
* @private
|
|
@@ -3083,8 +3033,4 @@ class CanvasFramework {
|
|
|
3083
3033
|
}
|
|
3084
3034
|
}
|
|
3085
3035
|
|
|
3086
|
-
export default CanvasFramework;
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3036
|
+
export default CanvasFramework;
|