canvasframework 0.5.56 → 0.5.57
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/QRCodeGenerator.js +207 -131
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Component from '../core/Component.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* QRCodeGenerator SANS librairie externe
|
|
5
|
+
* Version simplifiée qui fonctionne pour texte court et URLs
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* new QRCodeGenerator(framework, {
|
|
@@ -18,158 +18,243 @@ class QRCodeGenerator extends Component {
|
|
|
18
18
|
super(framework, options);
|
|
19
19
|
|
|
20
20
|
// Données du QR code
|
|
21
|
-
this.data = options.data || options.text || '
|
|
21
|
+
this.data = options.data || options.text || 'Hello';
|
|
22
22
|
|
|
23
23
|
// Dimensions
|
|
24
24
|
this.width = options.width || 250;
|
|
25
25
|
this.height = options.height || 250;
|
|
26
|
-
|
|
27
|
-
// Le QR code est toujours carré, on prend la plus petite dimension
|
|
28
26
|
this.qrSize = Math.min(this.width, this.height);
|
|
29
27
|
|
|
30
|
-
// Options
|
|
31
|
-
this.errorCorrectionLevel = options.errorCorrectionLevel || 'M';
|
|
28
|
+
// Options visuelles
|
|
32
29
|
this.backgroundColor = options.backgroundColor || '#ffffff';
|
|
33
30
|
this.foregroundColor = options.foregroundColor || '#000000';
|
|
34
|
-
this.
|
|
31
|
+
this.padding = options.padding || 20;
|
|
35
32
|
|
|
36
|
-
// État
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
39
|
-
this.isGenerating = false;
|
|
40
|
-
this.error = null;
|
|
33
|
+
// État
|
|
34
|
+
this.qrMatrix = null;
|
|
35
|
+
this.moduleSize = 0;
|
|
41
36
|
|
|
42
|
-
//
|
|
43
|
-
this.
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Charge la librairie qrcode.js depuis CDN
|
|
48
|
-
*/
|
|
49
|
-
loadQRCodeLibrary() {
|
|
50
|
-
if (typeof QRCode === 'undefined') {
|
|
51
|
-
const script = document.createElement('script');
|
|
52
|
-
script.src = 'https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js';
|
|
53
|
-
script.onload = () => {
|
|
54
|
-
console.log('[QRCodeGenerator] Library loaded');
|
|
55
|
-
this.generateQRCode();
|
|
56
|
-
};
|
|
57
|
-
script.onerror = () => {
|
|
58
|
-
console.error('[QRCodeGenerator] Failed to load library');
|
|
59
|
-
this.error = 'Échec du chargement';
|
|
60
|
-
this.markDirty();
|
|
61
|
-
};
|
|
62
|
-
document.head.appendChild(script);
|
|
63
|
-
} else {
|
|
64
|
-
// Librairie déjà chargée
|
|
65
|
-
this.generateQRCode();
|
|
66
|
-
}
|
|
37
|
+
// Générer immédiatement
|
|
38
|
+
this.generateQRCode();
|
|
67
39
|
}
|
|
68
40
|
|
|
69
41
|
async _mount() {
|
|
70
42
|
super._mount?.();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (this.data && typeof QRCode !== 'undefined' && !this.qrCanvas) {
|
|
74
|
-
await this.generateQRCode();
|
|
43
|
+
if (!this.qrMatrix) {
|
|
44
|
+
this.generateQRCode();
|
|
75
45
|
}
|
|
76
46
|
}
|
|
77
47
|
|
|
78
48
|
destroy() {
|
|
79
|
-
|
|
80
|
-
this.qrCanvas = null;
|
|
81
|
-
}
|
|
82
|
-
if (this.qrImage) {
|
|
83
|
-
this.qrImage = null;
|
|
84
|
-
}
|
|
49
|
+
this.qrMatrix = null;
|
|
85
50
|
super.destroy?.();
|
|
86
51
|
}
|
|
87
52
|
|
|
88
53
|
onUnmount() {
|
|
89
54
|
console.log('[QRCodeGenerator] onUnmount called');
|
|
90
|
-
|
|
91
|
-
this.qrCanvas = null;
|
|
92
|
-
}
|
|
93
|
-
if (this.qrImage) {
|
|
94
|
-
this.qrImage = null;
|
|
95
|
-
}
|
|
55
|
+
this.qrMatrix = null;
|
|
96
56
|
}
|
|
97
57
|
|
|
98
58
|
/**
|
|
99
|
-
* Génère
|
|
59
|
+
* Génère un QR code simple (Version 1 : 21x21)
|
|
100
60
|
*/
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
61
|
+
generateQRCode() {
|
|
62
|
+
try {
|
|
63
|
+
// Taille fixe pour Version 1 (supporte ~25 caractères)
|
|
64
|
+
const size = 21;
|
|
65
|
+
|
|
66
|
+
// Créer la matrice
|
|
67
|
+
this.qrMatrix = this.createMatrix(size);
|
|
68
|
+
|
|
69
|
+
// Ajouter les patterns de base
|
|
70
|
+
this.addFinderPatterns(this.qrMatrix);
|
|
71
|
+
this.addTimingPatterns(this.qrMatrix);
|
|
72
|
+
this.addAlignmentPattern(this.qrMatrix);
|
|
73
|
+
|
|
74
|
+
// Ajouter les données (simplifié)
|
|
75
|
+
this.addDataSimple(this.qrMatrix);
|
|
76
|
+
|
|
77
|
+
// Calculer la taille d'un module
|
|
78
|
+
const availableSize = this.qrSize - (this.padding * 2);
|
|
79
|
+
this.moduleSize = Math.floor(availableSize / size);
|
|
80
|
+
|
|
81
|
+
console.log('[QRCodeGenerator] QR Code generated:', size + 'x' + size);
|
|
104
82
|
this.markDirty();
|
|
105
|
-
|
|
83
|
+
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error('[QRCodeGenerator] Error:', err);
|
|
106
86
|
}
|
|
87
|
+
}
|
|
107
88
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Crée une matrice vide
|
|
91
|
+
*/
|
|
92
|
+
createMatrix(size) {
|
|
93
|
+
const matrix = [];
|
|
94
|
+
for (let i = 0; i < size; i++) {
|
|
95
|
+
matrix[i] = new Array(size).fill(-1); // -1 = non défini
|
|
112
96
|
}
|
|
97
|
+
return matrix;
|
|
98
|
+
}
|
|
113
99
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Ajoute les patterns de position (3 carrés dans les coins)
|
|
102
|
+
*/
|
|
103
|
+
addFinderPatterns(matrix) {
|
|
104
|
+
const size = matrix.length;
|
|
105
|
+
|
|
106
|
+
// Pattern 7x7
|
|
107
|
+
const addFinder = (startX, startY) => {
|
|
108
|
+
for (let dy = 0; dy < 7; dy++) {
|
|
109
|
+
for (let dx = 0; dx < 7; dx++) {
|
|
110
|
+
const x = startX + dx;
|
|
111
|
+
const y = startY + dy;
|
|
112
|
+
|
|
113
|
+
// Carré extérieur (7x7)
|
|
114
|
+
if (dx === 0 || dx === 6 || dy === 0 || dy === 6) {
|
|
115
|
+
matrix[y][x] = 1;
|
|
116
|
+
}
|
|
117
|
+
// Carré intérieur (3x3) centré
|
|
118
|
+
else if (dx >= 2 && dx <= 4 && dy >= 2 && dy <= 4) {
|
|
119
|
+
matrix[y][x] = 1;
|
|
120
|
+
}
|
|
121
|
+
// Blanc entre les deux
|
|
122
|
+
else {
|
|
123
|
+
matrix[y][x] = 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
121
127
|
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
// Séparateurs blancs (1 pixel autour)
|
|
129
|
+
for (let i = 0; i < 8; i++) {
|
|
130
|
+
if (startX === 0 && startY === 0) {
|
|
131
|
+
// Haut gauche
|
|
132
|
+
if (i < 7) matrix[7][i] = 0;
|
|
133
|
+
if (i < 7) matrix[i][7] = 0;
|
|
134
|
+
} else if (startX === size - 7 && startY === 0) {
|
|
135
|
+
// Haut droit
|
|
136
|
+
if (i < 7) matrix[7][size - 8 + i] = 0;
|
|
137
|
+
if (i < 7) matrix[i][size - 8] = 0;
|
|
138
|
+
} else if (startX === 0 && startY === size - 7) {
|
|
139
|
+
// Bas gauche
|
|
140
|
+
if (i < 7) matrix[size - 8][i] = 0;
|
|
141
|
+
if (i < 7) matrix[size - 8 + i][7] = 0;
|
|
130
142
|
}
|
|
131
|
-
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// Haut gauche
|
|
147
|
+
addFinder(0, 0);
|
|
148
|
+
|
|
149
|
+
// Haut droit
|
|
150
|
+
addFinder(size - 7, 0);
|
|
151
|
+
|
|
152
|
+
// Bas gauche
|
|
153
|
+
addFinder(0, size - 7);
|
|
154
|
+
}
|
|
132
155
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
156
|
+
/**
|
|
157
|
+
* Ajoute les lignes de timing
|
|
158
|
+
*/
|
|
159
|
+
addTimingPatterns(matrix) {
|
|
160
|
+
const size = matrix.length;
|
|
161
|
+
|
|
162
|
+
// Ligne horizontale (ligne 6)
|
|
163
|
+
for (let i = 8; i < size - 8; i++) {
|
|
164
|
+
matrix[6][i] = (i % 2 === 0) ? 1 : 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Ligne verticale (colonne 6)
|
|
168
|
+
for (let i = 8; i < size - 8; i++) {
|
|
169
|
+
matrix[i][6] = (i % 2 === 0) ? 1 : 0;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
146
172
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Ajoute le pattern d'alignement (pour Version 1, pas nécessaire, mais on le met quand même)
|
|
175
|
+
*/
|
|
176
|
+
addAlignmentPattern(matrix) {
|
|
177
|
+
// Pour Version 1, pas d'alignment pattern
|
|
178
|
+
// On remplit juste avec le module noir obligatoire
|
|
179
|
+
const size = matrix.length;
|
|
180
|
+
matrix[size - 8][8] = 1; // Module noir fixe
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Ajoute les données de manière simplifiée (pattern basé sur les données)
|
|
185
|
+
*/
|
|
186
|
+
addDataSimple(matrix) {
|
|
187
|
+
const size = matrix.length;
|
|
188
|
+
|
|
189
|
+
// Convertir les données en une séquence de bits basique
|
|
190
|
+
let bits = '';
|
|
191
|
+
for (let i = 0; i < this.data.length; i++) {
|
|
192
|
+
const charCode = this.data.charCodeAt(i);
|
|
193
|
+
// Convertir en binaire sur 8 bits
|
|
194
|
+
bits += charCode.toString(2).padStart(8, '0');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Ajouter un padding si nécessaire
|
|
198
|
+
while (bits.length < 100) {
|
|
199
|
+
bits += '0';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let bitIndex = 0;
|
|
203
|
+
|
|
204
|
+
// Remplir la matrice en zigzag (de droite à gauche, de bas en haut)
|
|
205
|
+
for (let col = size - 1; col > 0; col -= 2) {
|
|
206
|
+
// Sauter la colonne de timing
|
|
207
|
+
if (col === 6) col--;
|
|
208
|
+
|
|
209
|
+
for (let row = 0; row < size; row++) {
|
|
210
|
+
// Alterner direction : bas->haut puis haut->bas
|
|
211
|
+
const y = ((col + 1) % 4 < 2) ? (size - 1 - row) : row;
|
|
212
|
+
|
|
213
|
+
// Remplir 2 colonnes à la fois
|
|
214
|
+
for (let c = 0; c < 2; c++) {
|
|
215
|
+
const x = col - c;
|
|
216
|
+
|
|
217
|
+
// Ne pas écraser les patterns existants
|
|
218
|
+
if (matrix[y][x] !== -1) continue;
|
|
219
|
+
|
|
220
|
+
// Placer un bit
|
|
221
|
+
if (bitIndex < bits.length) {
|
|
222
|
+
matrix[y][x] = bits[bitIndex] === '1' ? 1 : 0;
|
|
223
|
+
bitIndex++;
|
|
224
|
+
} else {
|
|
225
|
+
matrix[y][x] = 0;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Remplir les cases restantes avec 0
|
|
232
|
+
for (let y = 0; y < size; y++) {
|
|
233
|
+
for (let x = 0; x < size; x++) {
|
|
234
|
+
if (matrix[y][x] === -1) {
|
|
235
|
+
matrix[y][x] = 0;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
152
238
|
}
|
|
153
239
|
}
|
|
154
240
|
|
|
155
241
|
/**
|
|
156
|
-
* Change les données et régénère
|
|
242
|
+
* Change les données et régénère
|
|
157
243
|
*/
|
|
158
244
|
async setData(newData) {
|
|
159
245
|
if (this.data === newData) return;
|
|
160
|
-
|
|
161
246
|
this.data = newData;
|
|
162
|
-
|
|
247
|
+
this.generateQRCode();
|
|
163
248
|
}
|
|
164
249
|
|
|
165
250
|
/**
|
|
166
|
-
* Change la taille
|
|
251
|
+
* Change la taille
|
|
167
252
|
*/
|
|
168
253
|
async setSize(newSize) {
|
|
169
254
|
this.width = newSize;
|
|
170
255
|
this.height = newSize;
|
|
171
256
|
this.qrSize = newSize;
|
|
172
|
-
|
|
257
|
+
this.generateQRCode();
|
|
173
258
|
}
|
|
174
259
|
|
|
175
260
|
draw(ctx) {
|
|
@@ -179,12 +264,8 @@ class QRCodeGenerator extends Component {
|
|
|
179
264
|
ctx.fillStyle = this.backgroundColor;
|
|
180
265
|
ctx.fillRect(this.x, this.y, this.width, this.height);
|
|
181
266
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const offsetY = (this.height - this.qrSize) / 2;
|
|
185
|
-
|
|
186
|
-
// État : Génération en cours
|
|
187
|
-
if (this.isGenerating) {
|
|
267
|
+
if (!this.qrMatrix || this.moduleSize === 0) {
|
|
268
|
+
// Message de chargement
|
|
188
269
|
ctx.fillStyle = '#666';
|
|
189
270
|
ctx.font = '14px Arial';
|
|
190
271
|
ctx.textAlign = 'center';
|
|
@@ -198,30 +279,25 @@ class QRCodeGenerator extends Component {
|
|
|
198
279
|
return;
|
|
199
280
|
}
|
|
200
281
|
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
ctx.textAlign = 'center';
|
|
206
|
-
ctx.textBaseline = 'middle';
|
|
207
|
-
ctx.fillText(
|
|
208
|
-
this.error,
|
|
209
|
-
this.x + this.width / 2,
|
|
210
|
-
this.y + this.height / 2
|
|
211
|
-
);
|
|
212
|
-
ctx.restore();
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
282
|
+
// Centrer le QR code
|
|
283
|
+
const qrPixelSize = this.qrMatrix.length * this.moduleSize;
|
|
284
|
+
const offsetX = (this.width - qrPixelSize) / 2;
|
|
285
|
+
const offsetY = (this.height - qrPixelSize) / 2;
|
|
215
286
|
|
|
216
|
-
// Dessiner
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
this.y
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
287
|
+
// Dessiner les modules
|
|
288
|
+
ctx.fillStyle = this.foregroundColor;
|
|
289
|
+
|
|
290
|
+
for (let y = 0; y < this.qrMatrix.length; y++) {
|
|
291
|
+
for (let x = 0; x < this.qrMatrix[y].length; x++) {
|
|
292
|
+
if (this.qrMatrix[y][x] === 1) {
|
|
293
|
+
ctx.fillRect(
|
|
294
|
+
this.x + offsetX + x * this.moduleSize,
|
|
295
|
+
this.y + offsetY + y * this.moduleSize,
|
|
296
|
+
this.moduleSize,
|
|
297
|
+
this.moduleSize
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
225
301
|
}
|
|
226
302
|
|
|
227
303
|
ctx.restore();
|