Skip to content

Commit 718ba29

Browse files
authored
feat(module:qrcode): padding & background color for qrcode (#8001)
* fix(module:qrcode): padding & background color for qrcode * fix(module:qrcode): code refactoring * fix(module:qrcode): code refactoring * fix(module:qrcode): renaming bg color property name * fix(module:qrcode): provide tests
1 parent 10003db commit 718ba29

12 files changed

+166
-31
lines changed

components/qr-code/demo/background.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
order: 4
3+
title:
4+
zh-CN: 具有自定义背景颜色
5+
en-US: With custom background color
6+
---
7+
8+
## zh-CN
9+
10+
自定义二维码的背景颜色。
11+
12+
## en-US
13+
14+
Customize the background color of the QR Code.

components/qr-code/demo/background.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'nz-demo-qr-code-background',
5+
template: `
6+
<nz-qrcode nzBgColor="#ff6600" nzValue="https://ng.ant.design/"></nz-qrcode>
7+
<nz-qrcode nzBgColor="#1677ff" nzValue="https://ng.ant.design/"></nz-qrcode>
8+
`,
9+
styles: [
10+
`
11+
nz-qrcode {
12+
margin-right: 12px;
13+
background-color: #f6f6f6;
14+
}
15+
`
16+
]
17+
})
18+
export class NzDemoQrCodeBackgroundComponent {}

components/qr-code/demo/color.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
order: 3
2+
order: 5
33
title:
44
zh-CN: 自定义颜色
55
en-US: Custom Color

components/qr-code/demo/download.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
order: 5
2+
order: 7
33
title:
44
zh-CN: 下载二维码
55
en-US: Download QRCode

components/qr-code/demo/error-level.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
order: 4
2+
order: 6
33
title:
44
zh-CN: 容错等级
55
en-US: Error Level

components/qr-code/demo/padding.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
order: 3
3+
title:
4+
zh-CN: 带衬垫
5+
en-US: With padding
6+
---
7+
8+
## zh-CN
9+
10+
自定义 QR 码的填充。
11+
12+
## en-US
13+
14+
Customize the padding of the QR Code.

components/qr-code/demo/padding.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'nz-demo-qr-code-padding',
5+
template: `
6+
<nz-qrcode [nzPadding]="50" nzValue="https://ng.ant.design/"></nz-qrcode>
7+
<nz-qrcode [nzPadding]="[10, 20]" nzValue="https://ng.ant.design/"></nz-qrcode>
8+
`,
9+
styles: [
10+
`
11+
nz-qrcode {
12+
margin-right: 12px;
13+
background-color: #f6f6f6;
14+
}
15+
`
16+
]
17+
})
18+
export class NzDemoQrCodePaddingComponent {}

components/qr-code/doc/index.en-US.md

+13-11
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,19 @@ import { NzQRCodeModule } from 'ng-zorro-antd/qr-code';
1919

2020
### nz-qrcode
2121

22-
| Property | Description | Type | Default |
23-
| -------------- | ----------------------------------- | --------------------------------- | -------- |
24-
| `[nzValue]` | scanned link | `string` | - |
25-
| `[nzColor]` | QR code Color | `string` | `#000` |
26-
| `[nzSize]` | QR code Size | `number` | `160` |
27-
| `[nzIcon]` | Icon address in QR code | `string` | - |
28-
| `[nzIconSize]` | The size of the icon in the QR code | `number` | `40` |
29-
| `[nzBordered]` | Whether has border style | `boolean` | `true` |
30-
| `[nzStatus]` | QR code status | `'active'|'expired' |'loading'` | `active` |
31-
| `[nzLevel]` | Error Code Level | `'L'|'M'|'Q'|'H'` | `M` |
32-
| `(nzRefresh)` | callback | `EventEmitter<string>` | - |
22+
| Property | Description | Type | Default |
23+
|-----------------------|-------------------------------------|---------------------------------|----------|
24+
| `[nzValue]` | scanned link | `string` | - |
25+
| `[nzColor]` | QR code Color | `string` | `#000` |
26+
| `[nzBgColor]` | QR code background color | `string` | `#fff` |
27+
| `[nzSize]` | QR code Size | `number` | `160` |
28+
| `[nzPadding]` | QR code Padding | `number \| number[]` | `10` |
29+
| `[nzIcon]` | Icon address in QR code | `string` | - |
30+
| `[nzIconSize]` | The size of the icon in the QR code | `number` | `40` |
31+
| `[nzBordered]` | Whether has border style | `boolean` | `true` |
32+
| `[nzStatus]` | QR code status | `'active'|'expired' |'loading'` | `active` |
33+
| `[nzLevel]` | Error Code Level | `'L'|'M'|'Q'|'H'` | `M` |
34+
| `(nzRefresh)` | callback | `EventEmitter<string>` | - |
3335

3436
## Note
3537

components/qr-code/doc/index.zh-CN.md

+13-11
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,19 @@ import { NzQRCodeModule } from 'ng-zorro-antd/qr-code';
2020

2121
### nz-qrcode
2222

23-
| 参数 | 说明 | 类型 | 默认值 |
24-
| -------------- | -------------------- | --------------------------------- | -------- |
25-
| `[nzValue]` | 扫描后的地址 | `string` | - |
26-
| `[nzColor]` | 二维码颜色 | `string` | `#000` |
27-
| `[nzSize]` | 二维码大小 | `number` | `160` |
28-
| `[nzIcon]` | 二维码中 icon 地址 | `string` | - |
29-
| `[nzIconSize]` | 二维码中 icon 大小 | `number` | `40` |
30-
| `[nzBordered]` | 是否有边框 | `boolean` | `true` |
31-
| `[nzStatus]` | 二维码状态 | `'active'|'expired' |'loading'` | `active` |
32-
| `[nzLevel]` | 二维码容错等级 | `'L'|'M'|'Q'|'H'` | `M` |
33-
| `(nzRefresh)` | 点击"点击刷新"的回调 | `EventEmitter<string>` | - |
23+
| 参数 | 说明 | 类型 | 默认值 |
24+
| -------------- |--------------|-----------------------------------| -------- |
25+
| `[nzValue]` | 扫描后的地址 | `string` | - |
26+
| `[nzColor]` | 二维码颜色 | `string` | `#000` |
27+
| `[nzBgColor]` | 二维码背景颜色 | `string` | `#fff` |
28+
| `[nzSize]` | 二维码大小 | `number` | `160` |
29+
| `[nzPadding]` | 二维码填充 | `number \| number[]` | `10` |
30+
| `[nzIcon]` | 二维码中 icon 地址 | `string` | - |
31+
| `[nzIconSize]` | 二维码中 icon 大小 | `number` | `40` |
32+
| `[nzBordered]` | 是否有边框 | `boolean` | `true` |
33+
| `[nzStatus]` | 二维码状态 | `'active'|'expired' |'loading'` | `active` |
34+
| `[nzLevel]` | 二维码容错等级 | `'L'|'M'|'Q'|'H'` | `M` |
35+
| `(nzRefresh)` | 点击"点击刷新"的回调 | `EventEmitter<string>` | - |
3436

3537
## 注意
3638

components/qr-code/qrcode.component.ts

+4
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ import { drawCanvas, ERROR_LEVEL_MAP, plotQRCodeData } from './qrcode';
5555
export class NzQRCodeComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy {
5656
@ViewChild('canvas', { static: false }) canvas!: ElementRef<HTMLCanvasElement>;
5757
@Input() nzValue: string = '';
58+
@Input() nzPadding: number | number[] = 10;
5859
@Input() nzColor: string = '#000000';
60+
@Input() nzBgColor: string = '#FFFFFF';
5961
@Input() nzSize: number = 160;
6062
@Input() nzIcon: string = '';
6163
@Input() nzIconSize: number = 40;
@@ -110,7 +112,9 @@ export class NzQRCodeComponent implements OnInit, AfterViewInit, OnChanges, OnDe
110112
plotQRCodeData(this.nzValue, this.nzLevel),
111113
this.nzSize,
112114
10,
115+
this.nzPadding,
113116
this.nzColor,
117+
this.nzBgColor,
114118
this.nzIconSize,
115119
this.nzIcon
116120
);

components/qr-code/qrcode.spec.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import * as QrCode from './qrcode';
2+
3+
describe('qr-code', () => {
4+
describe('format padding', () => {
5+
it('as number', () => {
6+
const padding = 10;
7+
expect(QrCode.formatPadding(padding)).toEqual([10, 10, 10, 10]);
8+
});
9+
10+
it('as array of 1', () => {
11+
const padding = [10];
12+
expect(QrCode.formatPadding(padding)).toEqual([10, 10, 10, 10]);
13+
});
14+
15+
it('as array of 2', () => {
16+
const padding = [10, 5];
17+
expect(QrCode.formatPadding(padding)).toEqual([10, 5, 10, 5]);
18+
});
19+
20+
it('as array of 4', () => {
21+
const padding = [10, 5, 4, 10];
22+
expect(QrCode.formatPadding(padding)).toEqual([10, 5, 4, 10]);
23+
});
24+
});
25+
});

components/qr-code/qrcode.ts

+44-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ export const ERROR_LEVEL_MAP: { [index in 'L' | 'M' | 'Q' | 'H']: qrcodegen.QrCo
1414

1515
const DEFAULT_SIZE = 160;
1616
const DEFAULT_SCALE = 10;
17+
const DEFAULT_PADDING = 10;
1718
const DEFAULT_COLOR = '#000000';
19+
const DEFAULT_BACKGROUND_COLOR = '#FFFFFF';
1820
const DEFAULT_ICONSIZE = 40;
1921
const DEFAULT_LEVEL: keyof typeof ERROR_LEVEL_MAP = 'M';
2022

@@ -33,30 +35,35 @@ export function drawCanvas(
3335
value: qrcodegen.QrCode | null,
3436
size = DEFAULT_SIZE,
3537
scale = DEFAULT_SCALE,
38+
padding: number | number[] = DEFAULT_PADDING,
3639
color = DEFAULT_COLOR,
40+
backgroundColor = DEFAULT_BACKGROUND_COLOR,
3741
iconSize = DEFAULT_ICONSIZE,
3842
icon?: string
3943
): void {
4044
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
45+
const formattedPadding = formatPadding(padding);
4146
canvas.style.width = `${size}px`;
4247
canvas.style.height = `${size}px`;
4348
if (!value) {
4449
ctx.fillStyle = 'rgba(0, 0, 0, 0)';
4550
ctx.fillRect(0, 0, canvas.width, canvas.height);
4651
return;
4752
}
48-
canvas.width = value.size * scale;
49-
canvas.height = value.size * scale;
53+
canvas.width = value.size * scale + formattedPadding[1] + formattedPadding[3];
54+
canvas.height = value.size * scale + formattedPadding[0] + formattedPadding[2];
5055
if (!icon) {
51-
drawCanvasColor(ctx, value, scale, color);
56+
drawCanvasBackground(ctx, canvas.width, canvas.height, scale, backgroundColor);
57+
drawCanvasColor(ctx, value, scale, formattedPadding, backgroundColor, color);
5258
} else {
5359
const iconImg = new Image();
5460
iconImg.src = icon;
5561
iconImg.crossOrigin = 'anonymous';
5662
iconImg.width = iconSize * (canvas.width / size);
5763
iconImg.height = iconSize * (canvas.width / size);
5864
iconImg.onload = () => {
59-
drawCanvasColor(ctx, value, scale, color);
65+
drawCanvasBackground(ctx, canvas.width, canvas.height, scale, backgroundColor);
66+
drawCanvasColor(ctx, value, scale, formattedPadding, backgroundColor, color);
6067
const iconCoordinate = canvas.width / 2 - (iconSize * (canvas.width / size)) / 2;
6168

6269
ctx.fillRect(iconCoordinate, iconCoordinate, iconSize * (canvas.width / size), iconSize * (canvas.width / size));
@@ -68,20 +75,51 @@ export function drawCanvas(
6875
iconSize * (canvas.width / size)
6976
);
7077
};
71-
iconImg.onerror = () => drawCanvasColor(ctx, value, scale, color);
78+
iconImg.onerror = () => {
79+
drawCanvasBackground(ctx, canvas.width, canvas.height, scale, backgroundColor);
80+
drawCanvasColor(ctx, value, scale, formattedPadding, backgroundColor, color);
81+
};
7282
}
7383
}
7484

7585
export function drawCanvasColor(
7686
ctx: CanvasRenderingContext2D,
7787
value: qrcodegen.QrCode,
7888
scale: number,
89+
padding: number[],
90+
backgroundColor: string,
7991
color: string
8092
): void {
8193
for (let y = 0; y < value.size; y++) {
8294
for (let x = 0; x < value.size; x++) {
83-
ctx.fillStyle = value.getModule(x, y) ? color : 'rgba(0, 0, 0, 0)';
95+
ctx.fillStyle = value.getModule(x, y) ? color : backgroundColor;
96+
ctx.fillRect(padding[3] + x * scale, padding[0] + y * scale, scale, scale);
97+
}
98+
}
99+
}
100+
101+
export function drawCanvasBackground(
102+
ctx: CanvasRenderingContext2D,
103+
width: number,
104+
height: number,
105+
scale: number,
106+
backgroundColor: string
107+
): void {
108+
for (let y = 0; y < height; y++) {
109+
for (let x = 0; x < width; x++) {
110+
ctx.fillStyle = backgroundColor;
84111
ctx.fillRect(x * scale, y * scale, scale, scale);
85112
}
86113
}
87114
}
115+
116+
export function formatPadding(padding: number | number[]): number[] {
117+
if (Array.isArray(padding)) {
118+
// Build an array of 4 elements and repeat values from padding as necessary to set the value of the array
119+
return Array(4)
120+
.fill(0)
121+
.map((_, index) => padding[index % padding.length]);
122+
} else {
123+
return [padding, padding, padding, padding];
124+
}
125+
}

0 commit comments

Comments
 (0)