@oguzhnatly/react-native-custom-qr-codes 2.0.0

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/lib/QRCode.js ADDED
@@ -0,0 +1,529 @@
1
+ /*
2
+
3
+ QRCode.js
4
+
5
+ This is a Customisable QR Code Component for React Native Applications.
6
+
7
+ --Geoff Natin 08/7/17 21:49
8
+
9
+ */
10
+
11
+ import { Image, View } from "react-native";
12
+ //-----------------------------Imports-----------------------------------
13
+ import React, { PureComponent } from "react";
14
+ import Svg, {
15
+ ClipPath,
16
+ Defs,
17
+ LinearGradient,
18
+ Rect,
19
+ Stop,
20
+ } from "react-native-svg";
21
+
22
+ import PropTypes from "prop-types";
23
+ import { drawPiece } from "./styles";
24
+ import { generateQRCode } from "./QRCodeGenerator.js";
25
+ import styled from "../../styled-components";
26
+
27
+ //-----------------------------Component---------------------------------
28
+ export default class QRCode extends PureComponent {
29
+ //-----------------------Properties---------------------
30
+ static propTypes = {
31
+ content: PropTypes.string,
32
+ size: PropTypes.number,
33
+ padding: PropTypes.number,
34
+ color: PropTypes.string,
35
+ linearGradient: PropTypes.arrayOf(PropTypes.string),
36
+ gradientDirection: PropTypes.arrayOf(PropTypes.number),
37
+ backgroundColor: PropTypes.string,
38
+ innerEyeStyle: PropTypes.oneOf([
39
+ "circle",
40
+ "circles",
41
+ "diamond",
42
+ "rounded",
43
+ "square",
44
+ ]),
45
+ outerEyeStyle: PropTypes.oneOf([
46
+ "circle",
47
+ "circles",
48
+ "diamond",
49
+ "rounded",
50
+ "square",
51
+ ]),
52
+ codeStyle: PropTypes.oneOf([
53
+ "circle",
54
+ "diamond",
55
+ "dot",
56
+ "ninja",
57
+ "sharp",
58
+ "square",
59
+ ]),
60
+ logo: PropTypes.oneOfType([Image.propTypes.source, PropTypes.string]),
61
+ backgroundImage: Image.propTypes.source,
62
+ logoSize: PropTypes.number,
63
+ ecl: PropTypes.oneOf(["L", "M", "Q", "H"]),
64
+ svg: PropTypes.any,
65
+ isRTL: PropTypes.bool,
66
+ };
67
+
68
+ static defaultProps = {
69
+ content: "No Content",
70
+ size: 250,
71
+ padding: 0,
72
+ color: "black",
73
+ gradientDirection: [0, 0, 170, 0],
74
+ backgroundColor: "transparent",
75
+ codeStyle: "square",
76
+ outerEyeStyle: "square",
77
+ innerEyeStyle: "square",
78
+ logoSize: 100,
79
+ ecl: "H",
80
+ isRTL: false,
81
+ };
82
+
83
+ //-----------------------Methods-----------------------
84
+
85
+ //Returns an array of SVG Elements that represent the pieces of the QR Code
86
+ getPieces() {
87
+ var qr = generateQRCode(this.props);
88
+
89
+ var modules = qr.qrcode.modules;
90
+
91
+ var size = this.props.size;
92
+ var length = modules.length;
93
+ var xsize = size / length;
94
+ var ysize = size / length;
95
+ var logoX = this.props.size / 2 - this.props.logoSize / 2;
96
+ var logoY = this.props.size / 2 - this.props.logoSize / 2;
97
+ var logoSize = this.props.logoSize;
98
+
99
+ var pieces = [];
100
+ var nonPieces = [];
101
+
102
+ this.length = length;
103
+ this.ratio = xsize;
104
+
105
+ //Add the SVG element of each piece in the body of the QR Code
106
+ for (var y = 0; y < length; y++) {
107
+ for (var x = 0; x < length; x++) {
108
+ var module = modules[x][y];
109
+ var px = x * xsize;
110
+ var py = y * ysize;
111
+
112
+ //TODO: Add function to compute if pieces overlap with circular logos (more complex. Must see if tl or br is inside the radius from the centre of the circle (pythagoras theorem?))
113
+ var overlapsWithLogo =
114
+ (px > logoX &&
115
+ px < logoX + logoSize &&
116
+ py > logoY &&
117
+ py < logoY + logoSize) || //Piece's top left is inside the logo area
118
+ (px + xsize > logoX &&
119
+ px + xsize < logoX + logoSize &&
120
+ py + ysize > logoY &&
121
+ py + ysize < logoY + logoSize); //Piece's bottom right is inside the logo area
122
+
123
+ if (!this.props.logo || (this.props.logo && !overlapsWithLogo)) {
124
+ if (module) {
125
+ pieces.push(this.getPiece(x, y, modules));
126
+ } else {
127
+ nonPieces.push(this.getPiece(x, y, modules));
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ if (this.props.backgroundImage) {
134
+ return (
135
+ <View
136
+ style={{
137
+ backgroundColor: "transparent",
138
+ margin: 0,
139
+ }}
140
+ >
141
+ <Image
142
+ source={this.props.backgroundImage}
143
+ style={{
144
+ position: "absolute",
145
+ top: 0,
146
+ left: 0,
147
+ height: this.props.logoSize,
148
+ width: this.props.logoSize,
149
+ }}
150
+ />
151
+ {this.displayLogo()}
152
+ <Svg
153
+ style={{
154
+ backgroundColor: "transparent",
155
+ height: this.props.logoSize,
156
+ width: this.props.logoSize,
157
+ }}
158
+ >
159
+ <Defs>
160
+ <ClipPath id="clip">{nonPieces}</ClipPath>
161
+ </Defs>
162
+ <Rect
163
+ clipPath="url(#clip)"
164
+ fill="transparent"
165
+ x={0}
166
+ y={0}
167
+ height="100%"
168
+ width="100%"
169
+ />
170
+ </Svg>
171
+ </View>
172
+ );
173
+ } else if (this.props.linearGradient) {
174
+ return (
175
+ <View>
176
+ <Svg
177
+ style={{
178
+ backgroundColor: this.props.backgroundColor,
179
+ height: this.props.size,
180
+ width: this.props.size,
181
+ }}
182
+ >
183
+ <Defs>
184
+ <ClipPath id="clip">{pieces}</ClipPath>
185
+ <LinearGradient
186
+ id="grad"
187
+ x1={this.props.gradientDirection[0]}
188
+ y1={this.props.gradientDirection[1]}
189
+ x2={this.props.gradientDirection[2]}
190
+ y2={this.props.gradientDirection[3]}
191
+ >
192
+ <Stop
193
+ offset="0"
194
+ stopColor={this.props.linearGradient[0]}
195
+ stopOpacity="1"
196
+ />
197
+ <Stop
198
+ offset="1"
199
+ stopColor={this.props.linearGradient[1]}
200
+ stopOpacity="1"
201
+ />
202
+ </LinearGradient>
203
+ </Defs>
204
+ <Rect
205
+ clipPath="url(#clip)"
206
+ fill="transparent"
207
+ x={0}
208
+ y={0}
209
+ height="100%"
210
+ width="100%"
211
+ fill="url(#grad)"
212
+ />
213
+ </Svg>
214
+ {this.displayLogo()}
215
+ </View>
216
+ );
217
+ } else {
218
+ return (
219
+ <View>
220
+ <Svg
221
+ style={{
222
+ backgroundColor: this.props.backgroundColor,
223
+ height: this.props.size,
224
+ width: this.props.size,
225
+ }}
226
+ >
227
+ <Defs>
228
+ <ClipPath id="clip">{pieces}</ClipPath>
229
+ </Defs>
230
+ <Rect
231
+ clipPath="url(#clip)"
232
+ fill="transparent"
233
+ x={0}
234
+ y={0}
235
+ height="100%"
236
+ width="100%"
237
+ fill={this.props.color}
238
+ />
239
+ </Svg>
240
+ {this.displayLogo()}
241
+ </View>
242
+ );
243
+ }
244
+ }
245
+
246
+ //Renders the logo on top of the QR Code if there is one
247
+ displayLogo() {
248
+ if (this.props.logo && !this.props.svg) {
249
+ return (
250
+ <Image
251
+ source={this.props.logo}
252
+ style={{
253
+ width: this.props.logoSize,
254
+ height: this.props.logoSize,
255
+ position: "absolute",
256
+ left: this.props.size / 2 - this.props.logoSize / 2,
257
+ top: this.props.size / 2 - this.props.logoSize / 2,
258
+ borderRadius: 20,
259
+ }}
260
+ />
261
+ );
262
+ } else {
263
+ return <View />;
264
+ }
265
+ }
266
+
267
+ //Returns an SVG Element that represents the piece of the QR code at modules[x][y]
268
+ getPiece(x, y, modules) {
269
+ //Find out which piece type it is
270
+ var pieceProps = this.getPieceProperties(x, y, modules);
271
+ return drawPiece(x, y, modules, pieceProps, this.props);
272
+ }
273
+
274
+ //Returns an object with orientation and pieceType representation of the piece type. (See https://github.com/mpaolino/qrlib/tree/master/qrlib/static)
275
+ getPieceProperties(x, y, modules) {
276
+ var mod_matrix = {};
277
+ mod_matrix.topLeft = x != 0 && y != 0 && modules[x - 1][y - 1];
278
+ mod_matrix.top = y != 0 && modules[x][y - 1];
279
+ mod_matrix.topRight =
280
+ x != modules.length - 1 && y != 0 && modules[x + 1][y - 1];
281
+ mod_matrix.left = x != 0 && modules[x - 1][y];
282
+ mod_matrix.right = x != modules.length - 1 && modules[x + 1][y];
283
+ mod_matrix.bottomLeft =
284
+ x != 0 && y != modules.length - 1 && modules[x - 1][y + 1];
285
+ mod_matrix.bottom = y != modules.length - 1 && modules[x][y + 1];
286
+ mod_matrix.bottomRight =
287
+ x != modules.length - 1 &&
288
+ y != modules.length - 1 &&
289
+ modules[x + 1][y + 1];
290
+
291
+ // (surroundingCount holds the number of pieces above or to the side of this piece)
292
+ var surroundingCount = 0;
293
+ if (mod_matrix.top) {
294
+ surroundingCount++;
295
+ }
296
+ if (mod_matrix.left) {
297
+ surroundingCount++;
298
+ }
299
+ if (mod_matrix.right) {
300
+ surroundingCount++;
301
+ }
302
+ if (mod_matrix.bottom) {
303
+ surroundingCount++;
304
+ }
305
+
306
+ var pieceProperties = {};
307
+ var orientation = 0;
308
+
309
+ //Determine what the piece properties are from its surrounding pieces.
310
+ // (surroundingCount holds the number of pieces above or to the side of this piece)
311
+ // (See https://github.com/mpaolino/qrlib/tree/master/qrlib/static)
312
+ switch (surroundingCount) {
313
+ case 0:
314
+ pieceProperties.pieceType = "1a";
315
+ if (mod_matrix.right) {
316
+ orientation = 90;
317
+ } else if (mod_matrix.bottom) {
318
+ orientation = 180;
319
+ } else if (mod_matrix.left) {
320
+ orientation = 270;
321
+ }
322
+ pieceProperties.orientation = orientation;
323
+ return pieceProperties;
324
+ case 1:
325
+ pieceProperties.pieceType = "2b";
326
+ pieceProperties.orientation = 0;
327
+ return pieceProperties;
328
+ case 2:
329
+ if (
330
+ (mod_matrix.top && mod_matrix.bottom) ||
331
+ (mod_matrix.left && mod_matrix.right)
332
+ ) {
333
+ var orientation = mod_matrix.top && mod_matrix.bottom ? 0 : 90;
334
+ pieceProperties.pieceType = "1b3b";
335
+ pieceProperties.orientation = orientation;
336
+ return pieceProperties;
337
+ } else {
338
+ var orientation = 0;
339
+ if (mod_matrix.top && mod_matrix.right) {
340
+ pieceProperties.orientation = 90;
341
+ pieceProperties.pieceType = mod_matrix.topRight ? "2a1b1a" : "2a1b";
342
+ return pieceProperties;
343
+ } else if (mod_matrix.right && mod_matrix.bottom) {
344
+ pieceProperties.orientation = 180;
345
+ pieceProperties.pieceType = mod_matrix.bottomRight
346
+ ? "2a1b1a"
347
+ : "2a1b";
348
+ return pieceProperties;
349
+ } else if (mod_matrix.left && mod_matrix.bottom) {
350
+ pieceProperties.orientation = 270;
351
+ pieceProperties.pieceType = mod_matrix.bottomLeft
352
+ ? "2a1b1a"
353
+ : "2a1b";
354
+ return pieceProperties;
355
+ } else {
356
+ pieceProperties.pieceType = mod_matrix.topLeft ? "2a1b1a" : "2a1b";
357
+ return pieceProperties;
358
+ }
359
+ }
360
+ case 3:
361
+ pieceProperties.pieceType = "2a1b2c";
362
+ var orientation = 0;
363
+ if (mod_matrix.top && mod_matrix.right && mod_matrix.bottom) {
364
+ orientation = 90;
365
+ } else if (mod_matrix.right && mod_matrix.bottom && mod_matrix.left) {
366
+ orientation = 180;
367
+ } else if (mod_matrix.bottom && mod_matrix.left && mod_matrix.top) {
368
+ orientation = 270;
369
+ }
370
+ pieceProperties.orientation = orientation;
371
+ return pieceProperties;
372
+ case 4:
373
+ pieceProperties.pieceType = "2a1b2c3b";
374
+ pieceProperties.orientation = 0;
375
+ return pieceProperties;
376
+ }
377
+ }
378
+
379
+ //---------------------Rendering-----------------------
380
+
381
+ render() {
382
+ let pieces = this.getPieces();
383
+ let eyeSize = 7 * this.ratio;
384
+ let eyeCoords = [
385
+ [0, 0], // top left
386
+ [0, this.props.size - eyeSize], // bottom left
387
+ [this.props.size - eyeSize, 0], // top right
388
+ ];
389
+
390
+ return (
391
+ <View>
392
+ <QRView size={this.props.size}>
393
+ {pieces}
394
+ {eyeCoords.map((eyeCoord, index) => (
395
+ <Eyes
396
+ key={index}
397
+ outerEyeStyle={this.props.outerEyeStyle}
398
+ innerEyeStyle={this.props.innerEyeStyle}
399
+ size={eyeSize}
400
+ color={this.props.color}
401
+ x={
402
+ this.props.isRTL
403
+ ? this.props.size - eyeCoord[0] - eyeSize
404
+ : eyeCoord[0]
405
+ }
406
+ y={eyeCoord[1]}
407
+ ratio={this.ratio}
408
+ />
409
+ ))}
410
+ </QRView>
411
+ <View
412
+ style={{
413
+ position: "absolute",
414
+ top: 0,
415
+ left: 0,
416
+ right: 0,
417
+ bottom: 0,
418
+ justifyContent: "center",
419
+ alignItems: "center",
420
+ }}
421
+ >
422
+ <View
423
+ style={{
424
+ position: "absolute",
425
+ backgroundColor: this.props.logo,
426
+ height: 60,
427
+ width: 60,
428
+ borderRadius: 20,
429
+ }}
430
+ />
431
+ {this.props.svg}
432
+ </View>
433
+ </View>
434
+ );
435
+ }
436
+ }
437
+
438
+ class Eyes extends React.Component {
439
+ constructor(props) {
440
+ super(props);
441
+ this.size = this.props.size;
442
+ this.ratio = this.props.ratio;
443
+
444
+ /* XY coordinates (right-to-bottom) */
445
+ this.x = this.props.x;
446
+ this.y = this.props.y;
447
+
448
+ this.outerEyeStyle = this.drawEyeStyle(this.props.outerEyeStyle, "outer");
449
+ this.innerEyeStyle = this.drawEyeStyle(this.props.innerEyeStyle, "inner");
450
+
451
+ this.color = this.props.color;
452
+
453
+ /* innerEye variables */
454
+ this.inSize = 3 * this.ratio;
455
+ this.inX = this.ratio;
456
+ this.inY = this.ratio;
457
+ }
458
+
459
+ drawEyeStyle(style, type) {
460
+ if (type === "outer") {
461
+ return ["diamond", "circles"].includes(style) ? null : style;
462
+ }
463
+ return ["diamond", "circles"].includes(style) ? null : style;
464
+ }
465
+
466
+ render() {
467
+ return (
468
+ <Container size={this.size}>
469
+ <EyeShape
470
+ type={this.outerEyeStyle}
471
+ size={this.size}
472
+ leftCornerX={this.x}
473
+ leftCornerY={this.y}
474
+ color={this.color}
475
+ border
476
+ borderWidth={this.ratio}
477
+ >
478
+ <EyeShape
479
+ type={this.innerEyeStyle}
480
+ size={this.inSize}
481
+ leftCornerX={this.inX}
482
+ leftCornerY={this.inY}
483
+ color={this.color}
484
+ borderWidth={this.ratio}
485
+ />
486
+ </EyeShape>
487
+ </Container>
488
+ );
489
+ }
490
+ }
491
+
492
+ const EyeShape = styled.View`
493
+ height: ${(props) => props.size}px;
494
+ width: ${(props) => props.size}px;
495
+ background-color: ${(props) =>
496
+ props.border || !props.type ? "transparent" : props.color};
497
+ top: ${(props) => props.leftCornerY}px;
498
+ left: ${(props) => props.leftCornerX}px;
499
+ border-radius: ${(props) => {
500
+ switch (props.type) {
501
+ case "circle":
502
+ return props.size;
503
+ case "rounded":
504
+ return props.size / 5;
505
+ default:
506
+ return 0;
507
+ }
508
+ }}px;
509
+ border-color: ${(props) =>
510
+ props.border && props.type ? props.color : "transparent"};
511
+ border-width: ${(props) => {
512
+ if (!props.border) {
513
+ return 0;
514
+ }
515
+ return props.borderWidth;
516
+ }}px;
517
+ `;
518
+
519
+ const Container = styled.View`
520
+ position: absolute;
521
+ height: ${(props) => props.size}px;
522
+ width: ${(props) => props.size}px;
523
+ `;
524
+
525
+ const QRView = styled.View`
526
+ position: relative;
527
+ height: ${(props) => props.size}px;
528
+ width: ${(props) => props.size}px;
529
+ `;