canvasframework 0.5.2 → 0.5.4

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.
@@ -45,12 +45,27 @@ class Drawer extends Component {
45
45
  // IMPORTANT: Définir les callbacks
46
46
  this.onPress = this.handlePress;
47
47
  this.onMove = this.handleMove;
48
+ // ✅ Se mettre automatiquement au-dessus de tous les composants
49
+ this.bringToFront();
50
+ }
51
+
52
+ /**
53
+ * Met le drawer au-dessus de tous les composants
54
+ * @private
55
+ */
56
+ bringToFront() {
57
+ const index = this.framework.components.indexOf(this);
58
+ if (index > -1) {
59
+ this.framework.components.splice(index, 1);
60
+ this.framework.components.push(this);
61
+ }
48
62
  }
49
63
 
50
64
  /**
51
65
  * Ouvre le drawer
52
66
  */
53
67
  open() {
68
+ this.bringToFront(); // ✅ Se remettre au-dessus à chaque ouverture
54
69
  this.visible = true;
55
70
  this.targetX = 0;
56
71
  this.animate();
@@ -363,30 +363,38 @@ class Tabs extends Component {
363
363
  ctx.restore();
364
364
 
365
365
  // ===== DESSINER LES ENFANTS DU TAB ACTIF =====
366
- const activeChildren = this.getActiveChildren();
367
-
368
- for (let child of activeChildren) {
369
- if (child.visible) {
370
- ctx.save();
371
-
372
- // Sauvegarder les coordonnées originales
373
- const originalX = child.x;
374
- const originalY = child.y;
375
-
376
- // Ajuster les coordonnées pour être absolues
377
- child.x = this.x + originalX;
378
- child.y = this.contentY + originalY;
379
-
380
- // Dessiner l'enfant
381
- child.draw(ctx);
382
-
383
- // Restaurer les coordonnées originales
384
- child.x = originalX;
385
- child.y = originalY;
386
-
387
- ctx.restore();
388
- }
389
- }
366
+ // ===== DESSINER LES ENFANTS DU TAB ACTIF =====
367
+ const activeChildren = this.getActiveChildren();
368
+ const scrollOffset = this.framework.scrollOffset || 0;
369
+
370
+ ctx.save();
371
+
372
+ // clip de la zone de contenu
373
+ ctx.beginPath();
374
+ ctx.rect(this.x, this.contentY, this.width, this.contentHeight);
375
+ ctx.clip();
376
+
377
+ for (let child of activeChildren) {
378
+ if (!child.visible) continue;
379
+
380
+ ctx.save();
381
+
382
+ const originalX = child.x;
383
+ const originalY = child.y;
384
+
385
+ // appliquer le scroll
386
+ child.x = this.x + originalX;
387
+ child.y = this.contentY + originalY - scrollOffset;
388
+
389
+ child.draw(ctx);
390
+
391
+ child.x = originalX;
392
+ child.y = originalY;
393
+
394
+ ctx.restore();
395
+ }
396
+
397
+ ctx.restore();
390
398
  }
391
399
 
392
400
  drawRipples(ctx, tabWidth) {
@@ -1,4 +1,5 @@
1
1
  import Component from '../core/Component.js';
2
+
2
3
  /**
3
4
  * Composant Skeleton (placeholder animé pendant le chargement)
4
5
  * @class
@@ -6,7 +7,7 @@ import Component from '../core/Component.js';
6
7
  * @param {Framework} framework - Instance du framework
7
8
  * @param {Object} [options={}] - Options de configuration
8
9
  * @param {string} [options.type='text'] - Type de skeleton ('text', 'circle', 'rectangle')
9
- * @param {number} [options.animationSpeed=0.03] - Vitesse de l'animation
10
+ * @param {number} [options.animationDuration=1.5] - Durée de l'animation en secondes
10
11
  * @example
11
12
  * const skeleton = new Skeleton(framework, {
12
13
  * type: 'text',
@@ -21,11 +22,14 @@ class Skeleton extends Component {
21
22
  constructor(framework, options = {}) {
22
23
  super(framework, options);
23
24
  /** @type {string} */
24
- this.type = options.type || 'text'; // text, circle, rectangle
25
+ this.type = options.type || 'text';
26
+ /** @type {number} */
27
+ this.animationDuration = (options.animationDuration || 1.5) * 1000; // en ms
25
28
  /** @type {number} */
26
- this.animationSpeed = options.animationSpeed || 0.03;
29
+ this.startTime = Date.now();
27
30
  /** @type {number} */
28
- this.animationProgress = 0;
31
+ this.borderRadius = options.borderRadius || 4;
32
+
29
33
  this.startAnimation();
30
34
  }
31
35
 
@@ -35,50 +39,155 @@ class Skeleton extends Component {
35
39
  */
36
40
  startAnimation() {
37
41
  const animate = () => {
38
- if (!this.visible) return;
39
-
40
- this.animationProgress += this.animationSpeed;
41
- if (this.animationProgress > Math.PI * 2) {
42
- this.animationProgress = 0;
42
+ if (!this.visible) {
43
+ requestAnimationFrame(animate);
44
+ return;
43
45
  }
44
-
45
46
  requestAnimationFrame(animate);
46
47
  };
47
48
  animate();
48
49
  }
49
50
 
50
51
  /**
51
- * Dessine le skeleton avec animation
52
+ * Calcule la progression de l'animation (0 à 1)
53
+ * @private
54
+ * @returns {number}
55
+ */
56
+ getAnimationProgress() {
57
+ const elapsed = Date.now() - this.startTime;
58
+ const progress = (elapsed % this.animationDuration) / this.animationDuration;
59
+ return progress;
60
+ }
61
+
62
+ /**
63
+ * Crée le gradient shimmer diagonal style Ionic/Material
64
+ * @private
65
+ * @param {CanvasRenderingContext2D} ctx
66
+ * @param {number} x
67
+ * @param {number} y
68
+ * @param {number} width
69
+ * @param {number} height
70
+ * @returns {CanvasGradient}
71
+ */
72
+ createShimmerGradient(ctx, x, y, width, height) {
73
+ const progress = this.getAnimationProgress();
74
+
75
+ // Calculer la diagonale pour l'effet
76
+ const diagonal = Math.sqrt(width * width + height * height);
77
+
78
+ // Position du gradient qui se déplace
79
+ const offset = diagonal * 2;
80
+ const position = -diagonal + (offset * progress);
81
+
82
+ // Angle à 45 degrés (de haut-gauche vers bas-droite)
83
+ const angle = Math.PI / 4;
84
+ const cos = Math.cos(angle);
85
+ const sin = Math.sin(angle);
86
+
87
+ const gradient = ctx.createLinearGradient(
88
+ x + position * cos,
89
+ y + position * sin,
90
+ x + (position + diagonal) * cos,
91
+ y + (position + diagonal) * sin
92
+ );
93
+
94
+ // Gradient blanc avec transparence (style Ionic/Material)
95
+ gradient.addColorStop(0, 'rgba(255, 255, 255, 0)');
96
+ gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.4)');
97
+ gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
98
+
99
+ return gradient;
100
+ }
101
+
102
+ /**
103
+ * Dessine un rectangle arrondi
104
+ * @private
105
+ */
106
+ roundRect(ctx, x, y, width, height, radius) {
107
+ if (width < 2 * radius) radius = width / 2;
108
+ if (height < 2 * radius) radius = height / 2;
109
+
110
+ ctx.beginPath();
111
+ ctx.moveTo(x + radius, y);
112
+ ctx.arcTo(x + width, y, x + width, y + height, radius);
113
+ ctx.arcTo(x + width, y + height, x, y + height, radius);
114
+ ctx.arcTo(x, y + height, x, y, radius);
115
+ ctx.arcTo(x, y, x + width, y, radius);
116
+ ctx.closePath();
117
+ }
118
+
119
+ /**
120
+ * Dessine le skeleton avec l'effet shimmer Ionic/Material
52
121
  * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
53
122
  */
54
123
  draw(ctx) {
55
124
  ctx.save();
56
125
 
57
- const brightness = 0.9 + 0.1 * Math.sin(this.animationProgress);
58
- ctx.fillStyle = `rgba(240, 240, 240, ${brightness})`;
126
+ // Couleur de fond (gris clair Material)
127
+ const backgroundColor = '#e0e0e0';
59
128
 
60
129
  switch(this.type) {
61
130
  case 'circle':
131
+ // Dessiner le cercle de base
132
+ ctx.fillStyle = backgroundColor;
62
133
  ctx.beginPath();
63
134
  ctx.arc(this.x + this.width/2, this.y + this.height/2, this.width/2, 0, Math.PI * 2);
64
135
  ctx.fill();
136
+
137
+ // Créer un clipping path pour le shimmer
138
+ ctx.beginPath();
139
+ ctx.arc(this.x + this.width/2, this.y + this.height/2, this.width/2, 0, Math.PI * 2);
140
+ ctx.clip();
141
+
142
+ // Appliquer le shimmer
143
+ ctx.fillStyle = this.createShimmerGradient(ctx, this.x, this.y, this.width, this.height);
144
+ ctx.fillRect(this.x, this.y, this.width, this.height);
65
145
  break;
146
+
66
147
  case 'text':
67
- // Simuler des lignes de texte
68
- const lineHeight = 20;
69
- const lines = Math.floor(this.height / lineHeight);
148
+ const lineHeight = 16;
149
+ const lineSpacing = 10;
150
+ const totalLineHeight = lineHeight + lineSpacing;
151
+ const lines = Math.floor(this.height / totalLineHeight);
70
152
 
71
153
  for (let i = 0; i < lines; i++) {
72
154
  const lineWidth = i === lines - 1 ? this.width * 0.6 : this.width;
73
- ctx.fillRect(this.x, this.y + i * lineHeight, lineWidth, 16);
155
+ const lineY = this.y + i * totalLineHeight;
156
+
157
+ // Fond de la ligne
158
+ ctx.fillStyle = backgroundColor;
159
+ this.roundRect(ctx, this.x, lineY, lineWidth, lineHeight, this.borderRadius);
160
+ ctx.fill();
161
+
162
+ // Shimmer sur la ligne
163
+ ctx.save();
164
+ this.roundRect(ctx, this.x, lineY, lineWidth, lineHeight, this.borderRadius);
165
+ ctx.clip();
166
+
167
+ ctx.fillStyle = this.createShimmerGradient(ctx, this.x, lineY, lineWidth, lineHeight);
168
+ ctx.fillRect(this.x - 100, lineY - 100, lineWidth + 200, lineHeight + 200);
169
+ ctx.restore();
74
170
  }
75
171
  break;
172
+
76
173
  default:
77
- ctx.fillRect(this.x, this.y, this.width, this.height);
174
+ // Rectangle de base
175
+ ctx.fillStyle = backgroundColor;
176
+ this.roundRect(ctx, this.x, this.y, this.width, this.height, this.borderRadius);
177
+ ctx.fill();
178
+
179
+ // Shimmer
180
+ ctx.save();
181
+ this.roundRect(ctx, this.x, this.y, this.width, this.height, this.borderRadius);
182
+ ctx.clip();
183
+
184
+ ctx.fillStyle = this.createShimmerGradient(ctx, this.x, this.y, this.width, this.height);
185
+ ctx.fillRect(this.x - 100, this.y - 100, this.width + 200, this.height + 200);
186
+ ctx.restore();
78
187
  }
79
188
 
80
189
  ctx.restore();
81
190
  }
82
191
  }
83
192
 
84
- export default Skeleton;
193
+ export default Skeleton;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasframework",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Canvas-based cross-platform UI framework (Material & Cupertino)",
5
5
  "type": "module",
6
6
  "main": "./index.js",