Skip to content

Commit fd675ed

Browse files
authored
chore: code optimization (#123)
1 parent b7a77b5 commit fd675ed

File tree

3 files changed

+142
-50
lines changed

3 files changed

+142
-50
lines changed

src/hooks/useQRCode.tsx

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,43 @@ import type { ErrorCorrectionLevel, ImageSettings } from '../interface';
33
import { ERROR_LEVEL_MAP, getImageSettings, getMarginSize } from '../utils';
44
import React from 'react';
55

6-
export const useQRCode = ({
7-
value,
8-
level,
9-
minVersion,
10-
includeMargin,
11-
marginSize,
12-
imageSettings,
13-
size,
14-
}: {
6+
interface Options {
157
value: string;
168
level: ErrorCorrectionLevel;
179
minVersion: number;
1810
includeMargin: boolean;
1911
marginSize?: number;
2012
imageSettings?: ImageSettings;
2113
size: number;
22-
}) => {
23-
const qrcode = React.useMemo<QrCode>(() => {
14+
}
15+
16+
export const useQRCode = (opt: Options) => {
17+
const {
18+
value,
19+
level,
20+
minVersion,
21+
includeMargin,
22+
marginSize,
23+
imageSettings,
24+
size,
25+
} = opt;
26+
27+
const memoizedQrcode = React.useMemo(() => {
2428
const segments = QrSegment.makeSegments(value);
2529
return QrCode.encodeSegments(segments, ERROR_LEVEL_MAP[level], minVersion);
2630
}, [value, level, minVersion]);
2731

28-
const { cells, margin, numCells, calculatedImageSettings } =
29-
React.useMemo(() => {
30-
const cs = qrcode.getModules();
31-
const mg = getMarginSize(includeMargin, marginSize);
32-
const ncs = cs.length + mg * 2;
33-
const cis = getImageSettings(cs, size, mg, imageSettings);
34-
return {
35-
cells: cs,
36-
margin: mg,
37-
numCells: ncs,
38-
calculatedImageSettings: cis,
39-
};
40-
}, [qrcode, size, imageSettings, includeMargin, marginSize]);
41-
42-
return {
43-
qrcode,
44-
margin,
45-
cells,
46-
numCells,
47-
calculatedImageSettings,
48-
};
32+
return React.useMemo(() => {
33+
const cs = memoizedQrcode.getModules();
34+
const mg = getMarginSize(includeMargin, marginSize);
35+
const ncs = cs.length + mg * 2;
36+
const cis = getImageSettings(cs, size, mg, imageSettings);
37+
return {
38+
cells: cs,
39+
margin: mg,
40+
numCells: ncs,
41+
calculatedImageSettings: cis,
42+
qrcode: memoizedQrcode,
43+
};
44+
}, [memoizedQrcode, size, imageSettings, includeMargin, marginSize]);
4945
};

src/interface.ts

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CSSProperties } from 'react';
1+
import type React from 'react';
22
import type { Ecc, QrCode } from './libs/qrcodegen';
33

44
export type Modules = ReturnType<QrCode['getModules']>;
@@ -11,27 +11,112 @@ export type ERROR_LEVEL_MAPPED_TYPE = {
1111
};
1212

1313
export type ImageSettings = {
14+
/**
15+
* The URI of the embedded image.
16+
*/
1417
src: string;
18+
/**
19+
* The height, in pixels, of the image.
20+
*/
1521
height: number;
22+
/**
23+
* The width, in pixels, of the image.
24+
*/
1625
width: number;
26+
/**
27+
* Whether or not to "excavate" the modules around the embedded image. This
28+
* means that any modules the embedded image overlaps will use the background
29+
* color.
30+
*/
1731
excavate: boolean;
32+
/**
33+
* The horiztonal offset of the embedded image, starting from the top left corner.
34+
* Will center if not specified.
35+
*/
1836
x?: number;
37+
/**
38+
* The vertical offset of the embedded image, starting from the top left corner.
39+
* Will center if not specified.
40+
*/
1941
y?: number;
42+
/**
43+
* The opacity of the embedded image in the range of 0-1.
44+
* @defaultValue 1
45+
*/
2046
opacity?: number;
47+
/**
48+
* The cross-origin value to use when loading the image. This is used to
49+
* ensure compatibility with CORS, particularly when extracting image data
50+
* from QRCodeCanvas.
51+
* Note: `undefined` is treated differently than the seemingly equivalent
52+
* empty string. This is intended to align with HTML behavior where omitting
53+
* the attribute behaves differently than the empty string.
54+
*/
2155
crossOrigin?: CrossOrigin;
2256
};
2357

2458
export type QRProps = {
59+
/**
60+
* The value to encode into the QR Code. An array of strings can be passed in
61+
* to represent multiple segments to further optimize the QR Code.
62+
*/
2563
value: string;
64+
/**
65+
* The size, in pixels, to render the QR Code.
66+
* @defaultValue 128
67+
*/
2668
size?: number;
69+
/**
70+
* The Error Correction Level to use.
71+
* @see https://www.qrcode.com/en/about/error_correction.html
72+
* @defaultValue L
73+
*/
2774
level?: ErrorCorrectionLevel;
75+
/**
76+
* The background color used to render the QR Code.
77+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
78+
* @defaultValue #FFFFFF
79+
*/
2880
bgColor?: string;
81+
/**
82+
* The foregtound color used to render the QR Code.
83+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
84+
* @defaultValue #000000
85+
*/
2986
fgColor?: string;
30-
style?: CSSProperties;
87+
/**
88+
* The style to apply to the QR Code.
89+
*/
90+
style?: React.CSSProperties;
91+
/**
92+
* Whether or not a margin of 4 modules should be rendered as a part of the
93+
* QR Code.
94+
* @deprecated Use `marginSize` instead.
95+
* @defaultValue false
96+
*/
3197
includeMargin?: boolean;
98+
/**
99+
* The number of _modules_ to use for margin. The QR Code specification
100+
* requires `4`, however you can specify any number. Values will be turned to
101+
* integers with `Math.floor`. Overrides `includeMargin` when both are specified.
102+
* @defaultValue 0
103+
*/
32104
marginSize?: number;
105+
/**
106+
* The settings for the embedded image.
107+
*/
33108
imageSettings?: ImageSettings;
109+
/**
110+
* The title to assign to the QR Code. Used for accessibility reasons.
111+
*/
34112
title?: string;
113+
/**
114+
* The minimum version used when encoding the QR Code. Valid values are 1-40
115+
* with higher values resulting in more complex QR Codes. The optimal
116+
* (lowest) version is determined for the `value` provided, using `minVersion`
117+
* as the lower bound.
118+
* @defaultValue 1
119+
*/
35120
minVersion?: number;
36121
};
37122

src/libs/qrcodegen.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -371,22 +371,24 @@ export class QrCode {
371371
if (
372372
boostEcl &&
373373
dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8
374-
)
374+
) {
375375
ecl = newEcl;
376+
}
376377
}
377378

378379
// Concatenate all segments to create the data bit string
379380
const bb: number[] = [];
380381
for (const seg of segs) {
381382
appendBits(seg.mode.modeBits, 4, bb);
382383
appendBits(seg.numChars, seg.mode.numCharCountBits(version), bb);
383-
for (const b of seg.getData()) bb.push(b);
384+
for (const b of seg.getData()) {
385+
bb.push(b);
386+
}
384387
}
385388
assert(bb.length == dataUsedBits);
386389

387390
// Add terminator and pad up to a byte if applicable
388-
const dataCapacityBits: number =
389-
QrCode.getNumDataCodewords(version, ecl) * 8;
391+
const dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8;
390392
assert(bb.length <= dataCapacityBits);
391393
appendBits(0, Math.min(4, dataCapacityBits - bb.length), bb);
392394
appendBits(0, (8 - (bb.length % 8)) % 8, bb);
@@ -397,15 +399,16 @@ export class QrCode {
397399
let padByte = 0xec;
398400
bb.length < dataCapacityBits;
399401
padByte ^= 0xec ^ 0x11
400-
)
402+
) {
401403
appendBits(padByte, 8, bb);
404+
}
402405

403406
// Pack bits numbero bytes in big endian
404407
const dataCodewords: number[] = [];
405-
while (dataCodewords.length * 8 < bb.length) dataCodewords.push(0);
406-
bb.forEach(
407-
(b: number, i: number) => (dataCodewords[i >>> 3] |= b << (7 - (i & 7))),
408-
);
408+
while (dataCodewords.length * 8 < bb.length) {
409+
dataCodewords.push(0);
410+
}
411+
bb.forEach((b, i) => (dataCodewords[i >>> 3] |= b << (7 - (i & 7))));
409412

410413
// Create the QR Code object
411414
return new QrCode(version, ecl, dataCodewords, mask);
@@ -459,7 +462,9 @@ export class QrCode {
459462
// Check scalar arguments
460463
if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION)
461464
throw new RangeError('Version value out of range');
462-
if (msk < -1 || msk > 7) throw new RangeError('Mask value out of range');
465+
if (msk < -1 || msk > 7) {
466+
throw new RangeError('Mask value out of range');
467+
}
463468
this.size = version * 4 + 17;
464469

465470
// Initialize both grids to be size*size arrays of Boolean false
@@ -558,7 +563,9 @@ export class QrCode {
558563
// Calculate error correction code and pack bits
559564
const data: number = (this.errorCorrectionLevel.formatBits << 3) | mask; // errCorrLvl is unumber2, mask is unumber3
560565
let rem: number = data;
561-
for (let i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >>> 9) * 0x537);
566+
for (let i = 0; i < 10; i++) {
567+
rem = (rem << 1) ^ ((rem >>> 9) * 0x537);
568+
}
562569
const bits = ((data << 10) | rem) ^ 0x5412; // unumber15
563570
assert(bits >>> 15 == 0);
564571

@@ -567,14 +574,16 @@ export class QrCode {
567574
this.setFunctionModule(8, 7, getBit(bits, 6));
568575
this.setFunctionModule(8, 8, getBit(bits, 7));
569576
this.setFunctionModule(7, 8, getBit(bits, 8));
570-
for (let i = 9; i < 15; i++)
577+
for (let i = 9; i < 15; i++) {
571578
this.setFunctionModule(14 - i, 8, getBit(bits, i));
572-
579+
}
573580
// Draw second copy
574-
for (let i = 0; i < 8; i++)
581+
for (let i = 0; i < 8; i++) {
575582
this.setFunctionModule(this.size - 1 - i, 8, getBit(bits, i));
576-
for (let i = 8; i < 15; i++)
583+
}
584+
for (let i = 8; i < 15; i++) {
577585
this.setFunctionModule(8, this.size - 15 + i, getBit(bits, i));
586+
}
578587
this.setFunctionModule(8, this.size - 8, true); // Always dark
579588
}
580589

@@ -587,7 +596,9 @@ export class QrCode {
587596

588597
// Calculate error correction code and pack bits
589598
let rem: number = this.version; // version is unumber6, in the range [7, 40]
590-
for (let i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >>> 11) * 0x1f25);
599+
for (let i = 0; i < 12; i++) {
600+
rem = (rem << 1) ^ ((rem >>> 11) * 0x1f25);
601+
}
591602
const bits: number = (this.version << 12) | rem; // unumber18
592603
assert(bits >>> 18 == 0);
593604

0 commit comments

Comments
 (0)