canvasframework 0.5.8 → 0.5.10

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  # Canvas UI Engine (UI engine inspired by Flutter, built for the Web)
5
5
 
6
6
  > **Canvas-based UI Engine for Mobile & Embedded Apps**
7
- > A high-performance UI engine rendered with Canvas/WebGL (Text rendering), running inside a WebView runtime (Capacitor / Cordova), without DOM, HTML or CSS.
7
+ > A high-performance UI engine rendered with Canvas/WebGL, running inside a WebView runtime (Capacitor / Cordova), without DOM, HTML or CSS.
8
8
 
9
9
  ---
10
10
 
@@ -1903,9 +1903,10 @@ class CanvasFramework {
1903
1903
  // ===== LANCER L'ANIMATION DE TRANSITION =====
1904
1904
 
1905
1905
  if (animate && !this.transitionState.isTransitioning) {
1906
- const transitionType = transition || route.transition || 'slide';
1907
- this.startTransition(oldComponents, this.components, transitionType, direction);
1908
- }
1906
+ const transitionType = transition || route.transition || 'slide';
1907
+ this.startTransition(oldComponents, this.components, transitionType, direction);
1908
+ // Pas besoin d'appeler renderFull() ici, la boucle de rendu s'en chargera
1909
+ }
1909
1910
 
1910
1911
  // ===== LIFECYCLE: APRÈS ÊTRE ENTRÉ DANS LA NOUVELLE ROUTE =====
1911
1912
 
@@ -2662,21 +2663,39 @@ class CanvasFramework {
2662
2663
  startRenderLoop() {
2663
2664
  let lastScrollOffset = this.scrollOffset;
2664
2665
  let lastRenderMode = 'full';
2666
+ let lastFrameTime = performance.now();
2665
2667
 
2666
- const render = () => {
2668
+ const render = (currentTime) => {
2669
+ // Calculer le delta time
2670
+ const deltaTime = currentTime - lastFrameTime;
2671
+ lastFrameTime = currentTime;
2672
+
2667
2673
  if (!this._splashFinished) {
2668
2674
  requestAnimationFrame(render);
2669
2675
  return;
2670
2676
  }
2671
2677
 
2672
- // Vérifier le scroll
2678
+ // 1. Mettre à jour l'animation de transition si active
2679
+ if (this.transitionState.isTransitioning) {
2680
+ this.updateTransition();
2681
+
2682
+ // Si la transition est terminée, nettoyer
2683
+ if (this.transitionState.progress >= 1) {
2684
+ this.transitionState.isTransitioning = false;
2685
+ this.transitionState.oldComponents = [];
2686
+ }
2687
+ }
2688
+
2689
+ // 2. Vérifier le scroll
2673
2690
  const scrollChanged = Math.abs(this.scrollOffset - lastScrollOffset) > 0.1;
2674
2691
  lastScrollOffset = this.scrollOffset;
2675
2692
 
2676
- // Décider du mode de rendu
2693
+ // 3. Décider du mode de rendu
2677
2694
  let renderMode = 'full';
2678
2695
 
2696
+ // Conditions pour utiliser le rendu optimisé (dirty components)
2679
2697
  if (this.optimizationEnabled &&
2698
+ !this.transitionState.isTransitioning && // ← Pas pendant les transitions
2680
2699
  this.dirtyComponents.size > 0 &&
2681
2700
  !this.isDragging &&
2682
2701
  Math.abs(this.scrollVelocity) < 0.5 &&
@@ -2685,17 +2704,23 @@ class CanvasFramework {
2685
2704
  renderMode = 'dirty';
2686
2705
  }
2687
2706
 
2688
- if (renderMode === 'full' || lastRenderMode !== renderMode) {
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
2689
2713
  this.ctx.fillStyle = this.backgroundColor || '#ffffff';
2690
2714
  this.ctx.fillRect(0, 0, this.width, this.height);
2691
2715
  this.renderFull();
2692
2716
  } else {
2717
+ // Rendu optimisé (seulement les composants sales)
2693
2718
  this._renderDirtyComponents();
2694
2719
  }
2695
2720
 
2696
2721
  lastRenderMode = renderMode;
2697
2722
 
2698
- // AJOUTER : Calcul et affichage du FPS
2723
+ // 5. Calculer et afficher le FPS
2699
2724
  this._frames++;
2700
2725
  const now = performance.now();
2701
2726
  const elapsed = now - this._lastFpsTime;
@@ -2706,7 +2731,7 @@ class CanvasFramework {
2706
2731
  this._lastFpsTime = now;
2707
2732
  }
2708
2733
 
2709
- // ✅ AJOUTER : Afficher le FPS si activé
2734
+ // Afficher le FPS si activé
2710
2735
  if (this.showFps) {
2711
2736
  this.ctx.save();
2712
2737
  this.ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
@@ -2719,74 +2744,231 @@ class CanvasFramework {
2719
2744
  this.ctx.restore();
2720
2745
  }
2721
2746
 
2722
- // ✅ AJOUTER : Indicateurs de débogage si activés
2747
+ // Indicateurs de débogage si activés
2723
2748
  if (this.debbug) {
2724
2749
  this.drawOverflowIndicators();
2725
2750
  }
2726
2751
 
2727
- // ✅ AJOUTER : Marquer le premier rendu
2752
+ // Marquer le premier rendu
2728
2753
  if (!this._firstRenderDone) {
2729
2754
  this._markFirstRender();
2730
2755
  }
2731
2756
 
2732
- // ✅ AJOUTER : Mettre à jour l'inertie si nécessaire
2757
+ // Mettre à jour l'inertie du scroll si nécessaire
2733
2758
  if (Math.abs(this.scrollVelocity) > 0.1 && !this.isDragging) {
2734
2759
  this.scrollWorker.postMessage({ type: 'UPDATE_INERTIA' });
2735
2760
  }
2736
2761
 
2762
+ // Nettoyer les composants sales après le rendu
2763
+ if (renderMode === 'full') {
2764
+ this.dirtyComponents.clear();
2765
+ }
2766
+
2737
2767
  requestAnimationFrame(render);
2738
2768
  };
2739
2769
 
2740
- render();
2770
+ render(lastFrameTime);
2771
+ }
2772
+
2773
+
2774
+ /**
2775
+ * Rendu spécial pour les transitions
2776
+ * @private
2777
+ */
2778
+ renderTransition() {
2779
+ const ctx = this.ctx;
2780
+
2781
+ // Effacer l'écran
2782
+ ctx.fillStyle = this.backgroundColor || '#ffffff';
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;
2788
+ const directionMultiplier = direction === 'forward' ? 1 : -1;
2789
+
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
+ }
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
+ }
2874
+ }
2875
+ }
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
+ }
2893
+ }
2894
+ }
2895
+ break;
2896
+ }
2741
2897
  }
2742
2898
 
2743
- // 3. Ajoutez une méthode renderFull() optimisée
2744
- renderFull() {
2745
- // Sauvegarder le contexte
2746
- this.ctx.save();
2747
-
2748
- // Séparer les composants
2749
- const scrollableComponents = [];
2750
- const fixedComponents = [];
2751
-
2752
- for (let comp of this.components) {
2753
- if (this.isFixedComponent(comp)) {
2754
- fixedComponents.push(comp);
2755
- } else {
2756
- scrollableComponents.push(comp);
2757
- }
2758
- }
2759
-
2760
- // ✅ OPTIMISATION : Dessiner les composants scrollables avec translation
2761
- if (scrollableComponents.length > 0) {
2762
- this.ctx.save();
2763
- this.ctx.translate(0, this.scrollOffset);
2764
-
2765
- for (let comp of scrollableComponents) {
2766
- if (comp.visible) {
2767
- // Viewport culling
2768
- const screenY = comp.y + this.scrollOffset;
2769
- const isInViewport = screenY + comp.height >= -100 && screenY <= this.height + 100;
2770
-
2771
- if (isInViewport) {
2772
- comp.draw(this.ctx);
2773
- }
2774
- }
2775
- }
2776
-
2777
- this.ctx.restore();
2778
- }
2899
+ /**
2900
+ * Mettre à jour la progression de la transition
2901
+ * @private
2902
+ */
2903
+ updateTransition() {
2904
+ if (!this.transitionState.isTransitioning) return;
2905
+
2906
+ const elapsed = Date.now() - this.transitionState.startTime;
2907
+ this.transitionState.progress = Math.min(elapsed / this.transitionState.duration, 1);
2908
+
2909
+ // Si la transition est terminée
2910
+ if (this.transitionState.progress >= 1) {
2911
+ this.transitionState.isTransitioning = false;
2912
+
2913
+ // Marquer tous les nouveaux composants comme sales pour le prochain rendu
2914
+ this.transitionState.newComponents.forEach(comp => {
2915
+ this.markComponentDirty(comp);
2916
+ });
2917
+ }
2918
+ }
2779
2919
 
2780
- // Dessiner les composants fixes
2781
- for (let comp of fixedComponents) {
2782
- if (comp.visible) {
2783
- comp.draw(this.ctx);
2784
- }
2785
- }
2920
+ /**
2921
+ * Rendu complet normal (sans transition)
2922
+ * @private
2923
+ */
2924
+ renderFull() {
2925
+ // Sauvegarder le contexte
2926
+ this.ctx.save();
2927
+
2928
+ // Séparer les composants
2929
+ const scrollableComponents = [];
2930
+ const fixedComponents = [];
2931
+
2932
+ for (let comp of this.components) {
2933
+ if (this.isFixedComponent(comp)) {
2934
+ fixedComponents.push(comp);
2935
+ } else {
2936
+ scrollableComponents.push(comp);
2937
+ }
2938
+ }
2939
+
2940
+ // Dessiner les composants scrollables avec translation
2941
+ if (scrollableComponents.length > 0) {
2942
+ this.ctx.save();
2943
+ this.ctx.translate(0, this.scrollOffset);
2944
+
2945
+ for (let comp of scrollableComponents) {
2946
+ if (comp.visible) {
2947
+ // Viewport culling
2948
+ const screenY = comp.y + this.scrollOffset;
2949
+ const isInViewport = screenY + comp.height >= -100 && screenY <= this.height + 100;
2950
+
2951
+ if (isInViewport) {
2952
+ comp.draw(this.ctx);
2953
+ }
2954
+ }
2955
+ }
2956
+
2957
+ this.ctx.restore();
2958
+ }
2959
+
2960
+ // Dessiner les composants fixes
2961
+ for (let comp of fixedComponents) {
2962
+ if (comp.visible) {
2963
+ comp.draw(this.ctx);
2964
+ }
2965
+ }
2966
+
2967
+ // Restaurer le contexte
2968
+ this.ctx.restore();
2969
+ }
2786
2970
 
2787
- // Restaurer le contexte
2788
- this.ctx.restore();
2789
- }
2971
+
2790
2972
 
2791
2973
  /**
2792
2974
  * Fait défiler à une position spécifique
package/package.json CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "name": "canvasframework",
3
- "version": "0.5.8",
3
+ "version": "0.5.10",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/beyons/CanvasFramework.git"
7
+ },
4
8
  "description": "Canvas-based cross-platform UI framework (Material & Cupertino)",
5
9
  "type": "module",
6
10
  "main": "./index.js",