canvasframework 0.5.10 → 0.5.12

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.
@@ -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 FloatedCamera autonome avec gestion directe des clics/touches
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 {
@@ -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 = (currentTime) => {
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
- // 1. Mettre à jour l'animation de transition si active
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
- this.updateTransition();
2690
+ // Mettre à jour la progression
2691
+ const elapsed = Date.now() - this.transitionState.startTime;
2692
+ this.transitionState.progress = Math.min(elapsed / this.transitionState.duration, 1);
2681
2693
 
2682
- // Si la transition est terminée, nettoyer
2694
+ // Rendu spécial pour la transition
2695
+ this.renderSimpleTransition();
2696
+
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
- // 2. Vérifier le scroll
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
- lastRenderMode = renderMode;
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,170 @@ class CanvasFramework {
2744
2728
  this.ctx.restore();
2745
2729
  }
2746
2730
 
2747
- // Indicateurs de débogage si activés
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(lastFrameTime);
2739
+ render();
2771
2740
  }
2772
-
2773
-
2741
+
2774
2742
  /**
2775
- * Rendu spécial pour les transitions
2776
- * @private
2743
+ * Rendu ultra simple pour la transition slide
2777
2744
  */
2778
- renderTransition() {
2779
- const ctx = this.ctx;
2745
+ renderSimpleTransition() {
2746
+ const { progress, type, direction, oldComponents, newComponents } = this.transitionState;
2780
2747
 
2781
- // Effacer l'écran
2782
- ctx.fillStyle = this.backgroundColor || '#ffffff';
2783
- ctx.fillRect(0, 0, this.width, this.height);
2748
+ // Easing pour une animation plus fluide
2749
+ const eased = this.easeInOutCubic(progress);
2784
2750
 
2785
- // Dessiner l'animation de transition
2786
- const progress = this.transitionState.progress;
2787
- const { type, direction, oldComponents, newComponents } = this.transitionState;
2788
2751
  const directionMultiplier = direction === 'forward' ? 1 : -1;
2789
2752
 
2790
- // Calculer le facteur d'easing
2791
- const eased = this.easeInOutCubic(progress);
2792
-
2793
- switch (type) {
2794
- case 'slide':
2795
- // 1. Ancienne vue qui sort
2796
- ctx.save();
2797
- ctx.translate(-this.width * eased * directionMultiplier, 0);
2798
- ctx.globalAlpha = 1 - eased * 0.3;
2799
-
2800
- for (let comp of oldComponents) {
2801
- if (comp && comp.visible) {
2802
- const isFixed = this.isFixedComponent(comp);
2803
-
2804
- if (isFixed) {
2805
- // Composants fixes : dessiner normalement
2806
- comp.draw(ctx);
2807
- } else {
2808
- // Composants scrollables : appliquer le décalage de scroll
2809
- ctx.save();
2810
- ctx.translate(0, this.scrollOffset);
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
- }
2753
+ if (type === 'slide') {
2754
+ // AMÉLIORATION 1 : Dessiner l'ANCIENNE vue qui sort
2755
+ this.ctx.save();
2756
+
2757
+ // L'ancienne vue se déplace vers la gauche/droite
2758
+ const oldOffset = -this.width * eased * directionMultiplier;
2759
+ this.ctx.translate(oldOffset, 0);
2760
+
2761
+ // Légère transparence pour donner de la profondeur
2762
+ this.ctx.globalAlpha = 1 - (eased * 0.3);
2763
+
2764
+ // Dessiner les anciens composants
2765
+ for (let comp of oldComponents) {
2766
+ if (comp && comp.visible) {
2767
+ const isFixed = this.isFixedComponent(comp);
2768
+
2769
+ if (isFixed) {
2770
+ comp.draw(this.ctx);
2771
+ } else {
2772
+ this.ctx.save();
2773
+ this.ctx.translate(0, 0); // Pas de scroll pendant la transition
2774
+ comp.draw(this.ctx);
2775
+ this.ctx.restore();
2834
2776
  }
2835
2777
  }
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
- }
2778
+ }
2779
+
2780
+ this.ctx.restore();
2781
+
2782
+ // AMÉLIORATION 2 : Dessiner la NOUVELLE vue qui entre
2783
+ this.ctx.save();
2784
+
2785
+ // La nouvelle vue arrive de la droite/gauche
2786
+ const newOffset = this.width * (1 - eased) * directionMultiplier;
2787
+ this.ctx.translate(newOffset, 0);
2788
+
2789
+ // Dessiner les nouveaux composants
2790
+ for (let comp of newComponents) {
2791
+ if (comp && comp.visible) {
2792
+ const isFixed = this.isFixedComponent(comp);
2793
+
2794
+ if (isFixed) {
2795
+ comp.draw(this.ctx);
2796
+ } else {
2797
+ this.ctx.save();
2798
+ this.ctx.translate(0, 0); // Pas de scroll pendant la transition
2799
+ comp.draw(this.ctx);
2800
+ this.ctx.restore();
2855
2801
  }
2856
2802
  }
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
- }
2803
+ }
2804
+
2805
+ this.ctx.restore();
2806
+ }
2807
+ else if (type === 'fade') {
2808
+ // Ancienne vue qui fade out
2809
+ this.ctx.save();
2810
+ this.ctx.globalAlpha = 1 - eased;
2811
+
2812
+ for (let comp of oldComponents) {
2813
+ if (comp && comp.visible) {
2814
+ const isFixed = this.isFixedComponent(comp);
2815
+ if (isFixed) {
2816
+ comp.draw(this.ctx);
2817
+ } else {
2818
+ this.ctx.save();
2819
+ this.ctx.translate(0, 0);
2820
+ comp.draw(this.ctx);
2821
+ this.ctx.restore();
2874
2822
  }
2875
2823
  }
2876
- ctx.restore();
2877
- break;
2878
-
2879
- case 'none':
2880
- // Pas d'animation, juste dessiner la nouvelle vue
2881
- for (let comp of newComponents) {
2882
- if (comp && comp.visible) {
2883
- const isFixed = this.isFixedComponent(comp);
2884
-
2885
- if (isFixed) {
2886
- comp.draw(ctx);
2887
- } else {
2888
- ctx.save();
2889
- ctx.translate(0, this.scrollOffset);
2890
- comp.draw(ctx);
2891
- ctx.restore();
2892
- }
2824
+ }
2825
+
2826
+ this.ctx.restore();
2827
+
2828
+ // Nouvelle vue qui fade in
2829
+ this.ctx.save();
2830
+ this.ctx.globalAlpha = eased;
2831
+
2832
+ for (let comp of newComponents) {
2833
+ if (comp && comp.visible) {
2834
+ const isFixed = this.isFixedComponent(comp);
2835
+ if (isFixed) {
2836
+ comp.draw(this.ctx);
2837
+ } else {
2838
+ this.ctx.save();
2839
+ this.ctx.translate(0, 0);
2840
+ comp.draw(this.ctx);
2841
+ this.ctx.restore();
2893
2842
  }
2894
2843
  }
2895
- break;
2844
+ }
2845
+
2846
+ this.ctx.restore();
2847
+ }
2848
+ else {
2849
+ this.renderFull();
2896
2850
  }
2897
2851
  }
2898
2852
 
2853
+ /**
2854
+ * Rendu normal (sans transition)
2855
+ */
2856
+ renderFull() {
2857
+ this.ctx.save();
2858
+
2859
+ // Séparer les composants fixes et scrollables
2860
+ const scrollableComponents = [];
2861
+ const fixedComponents = [];
2862
+
2863
+ for (let comp of this.components) {
2864
+ if (this.isFixedComponent(comp)) {
2865
+ fixedComponents.push(comp);
2866
+ } else {
2867
+ scrollableComponents.push(comp);
2868
+ }
2869
+ }
2870
+
2871
+ // Dessiner les composants scrollables
2872
+ if (scrollableComponents.length > 0) {
2873
+ this.ctx.save();
2874
+ this.ctx.translate(0, this.scrollOffset);
2875
+
2876
+ for (let comp of scrollableComponents) {
2877
+ if (comp.visible) {
2878
+ comp.draw(this.ctx);
2879
+ }
2880
+ }
2881
+
2882
+ this.ctx.restore();
2883
+ }
2884
+
2885
+ // Dessiner les composants fixes
2886
+ for (let comp of fixedComponents) {
2887
+ if (comp.visible) {
2888
+ comp.draw(this.ctx);
2889
+ }
2890
+ }
2891
+
2892
+ this.ctx.restore();
2893
+ }
2894
+
2899
2895
  /**
2900
2896
  * Mettre à jour la progression de la transition
2901
2897
  * @private
@@ -3083,8 +3079,4 @@ class CanvasFramework {
3083
3079
  }
3084
3080
  }
3085
3081
 
3086
- export default CanvasFramework;
3087
-
3088
-
3089
-
3090
-
3082
+ export default CanvasFramework;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasframework",
3
- "version": "0.5.10",
3
+ "version": "0.5.12",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/beyons/CanvasFramework.git"