WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 83bb521

Browse files
committed
feat(module:qrcode): support nzType && nzBoostLevel, delete nzPadding
1 parent 0e095a1 commit 83bb521

16 files changed

+837
-287
lines changed

components/qr-code/demo/padding.md

Lines changed: 0 additions & 14 deletions
This file was deleted.

components/qr-code/demo/type.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
order: 5
3+
title:
4+
zh-CN: 自定义渲染类型
5+
en-US: Custom Render Type
6+
---
7+
8+
## zh-CN
9+
10+
通过设置 `nzType` 自定义渲染结果,提供 `canvas``svg` 两个选项。
11+
12+
## en-US
13+
14+
Customize the rendering results by `nzType`, provide options `canvas` and `svg`.
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { Component } from '@angular/core';
33
import { NzQRCodeModule } from 'ng-zorro-antd/qr-code';
44

55
@Component({
6-
selector: 'nz-demo-qr-code-padding',
6+
selector: 'nz-demo-qr-code-type',
77
imports: [NzQRCodeModule],
88
template: `
9-
<nz-qrcode [nzPadding]="12" nzValue="https://ng.ant.design/"></nz-qrcode>
10-
<nz-qrcode [nzPadding]="[12, 24]" nzValue="https://ng.ant.design/"></nz-qrcode>
9+
<nz-qrcode nzValue="https://ng.ant.design/"></nz-qrcode>
10+
<nz-qrcode nzValue="https://ng.ant.design/" nzType="svg"></nz-qrcode>
1111
`,
1212
styles: [
1313
`
@@ -17,4 +17,4 @@ import { NzQRCodeModule } from 'ng-zorro-antd/qr-code';
1717
`
1818
]
1919
})
20-
export class NzDemoQrCodePaddingComponent {}
20+
export class NzDemoQrCodeTypeComponent {}

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

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,21 @@ Used when the text needs to be converted into a QR Code.
1515

1616
### nz-qrcode
1717

18-
| Property | Description | Type | Default |
19-
| ------------------ | ----------------------------------- | --------------------------------- | ----------- |
20-
| `[nzValue]` | scanned text | `string` | - |
21-
| `[nzColor]` | QR code Color | `string` | `'#000000'` |
22-
| `[nzBgColor]` | QR code background color | `string` | `'#FFFFFF'` |
23-
| `[nzSize]` | QR code Size | `number` | `160` |
24-
| `[nzPadding]` | QR code Padding | `number \| number[]` | `0` |
25-
| `[nzIcon]` | Icon address in QR code | `string` | - |
26-
| `[nzIconSize]` | The size of the icon in the QR code | `number` | `40` |
27-
| `[nzBordered]` | Whether has border style | `boolean` | `true` |
28-
| `[nzStatus]` | QR code status | `'active'|'expired' |'loading'` | `'active'` |
29-
| `[nzStatusRender]` | custom status | `TemplateRef<void> \| string` | - |
30-
| `[nzLevel]` | Error Code Level | `'L'|'M'|'Q'|'H'` | `'M'` |
31-
| `(nzRefresh)` | callback | `EventEmitter<string>` | - |
18+
| Property | Description | Type | Default | Version |
19+
| ------------------ | ------------------------------------------------------------------------------------------------------------ | --------------------------------- | ----------- | ----------------- |
20+
| `[nzValue]` | scanned text | `string \| string[]` | - | `string[]`:20.5.0 |
21+
| `[nzType]` | render type | `'canvas'\|'svg'` | `canvas` | 20.5.0 |
22+
| `[nzColor]` | QR code Color | `string` | `'#000000'` |
23+
| `[nzBgColor]` | QR code background color | `string` | `'#FFFFFF'` |
24+
| `[nzSize]` | QR code Size | `number` | `160` |
25+
| `[nzIcon]` | Icon address in QR code | `string` | - |
26+
| `[nzIconSize]` | The size of the icon in the QR code | `number` | `40` |
27+
| `[nzBordered]` | Whether has border style | `boolean` | `true` |
28+
| `[nzStatus]` | QR code status | `'active'|'expired' |'loading'` | `'active'` |
29+
| `[nzStatusRender]` | custom status | `TemplateRef<void> \| string` | - |
30+
| `[nzLevel]` | Error Code Level | `'L'|'M'|'Q'|'H'` | `'M'` |
31+
| `[nzBoostLevel]` | If enabled, the Error Correction Level of the result may be higher than the specified Error Correction Level | `boolean` | `true` | 20.5.0 |
32+
| `(nzRefresh)` | callback | `EventEmitter<string>` | - |
3233

3334
## Note
3435

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

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,21 @@ description: 能够将文本转换生成二维码的组件,支持自定义配
1616

1717
### nz-qrcode
1818

19-
| 参数 | 说明 | 类型 | 默认值 |
20-
| ------------------ | -------------------- | --------------------------------- | ----------- |
21-
| `[nzValue]` | 扫描后的文本 | `string` | - |
22-
| `[nzColor]` | 二维码颜色 | `string` | `'#000000'` |
23-
| `[nzBgColor]` | 二维码背景颜色 | `string` | `'#FFFFFF'` |
24-
| `[nzSize]` | 二维码大小 | `number` | `160` |
25-
| `[nzPadding]` | 二维码填充 | `number \| number[]` | `0` |
26-
| `[nzIcon]` | 二维码中 icon 地址 | `string` | - |
27-
| `[nzIconSize]` | 二维码中 icon 大小 | `number` | `40` |
28-
| `[nzBordered]` | 是否有边框 | `boolean` | `true` |
29-
| `[nzStatus]` | 二维码状态 | `'active'|'expired' |'loading'` | `'active'` |
30-
| `[nzStatusRender]` | 自定义状态渲染器 | `TemplateRef<void> \| string` | - |
31-
| `[nzLevel]` | 二维码容错等级 | `'L'|'M'|'Q'|'H'` | `'M'` |
32-
| `(nzRefresh)` | 点击"点击刷新"的回调 | `EventEmitter<string>` | - |
19+
| 参数 | 说明 | 类型 | 默认值 | 版本 |
20+
| ------------------ | ------------------------------------------------------------------ | --------------------------------- | ----------- | ----------------- |
21+
| `[nzValue]` | 扫描后的文本 | `string \| string[]` | - | `string[]`:20.5.0 |
22+
| `[nzType]` | 渲染类型 | `'canvas'\|'svg'` | `canvas` | 20.5.0 |
23+
| `[nzColor]` | 二维码颜色 | `string` | `'#000000'` |
24+
| `[nzBgColor]` | 二维码背景颜色 | `string` | `'#FFFFFF'` |
25+
| `[nzSize]` | 二维码大小 | `number` | `160` |
26+
| `[nzIcon]` | 二维码中 icon 地址 | `string` | - |
27+
| `[nzIconSize]` | 二维码中 icon 大小 | `number` | `40` |
28+
| `[nzBordered]` | 是否有边框 | `boolean` | `true` |
29+
| `[nzStatus]` | 二维码状态 | `'active'|'expired' |'loading'` | `'active'` |
30+
| `[nzStatusRender]` | 自定义状态渲染器 | `TemplateRef<void> \| string` | - |
31+
| `[nzLevel]` | 二维码容错等级 | `'L'|'M'|'Q'|'H'` | `'M'` |
32+
| `[nzBoostLevel]` | 如果启用,自动提升纠错等级,结果的纠错级别可能会高于指定的纠错级别 | `boolean` | `true` | 20.5.0 |
33+
| `(nzRefresh)` | 点击"点击刷新"的回调 | `EventEmitter<string>` | - |
3334

3435
## 注意
3536

components/qr-code/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55

66
export * from './qrcode.component';
77
export * from './qrcode.module';
8+
export * from './typing';
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/**
2+
* Use of this source code is governed by an MIT-style license that can be
3+
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
4+
*/
5+
6+
import { AfterViewInit, ChangeDetectionStrategy, Component, effect, ElementRef, input, ViewChild } from '@angular/core';
7+
8+
import { NzButtonModule } from 'ng-zorro-antd/button';
9+
import { NzIconModule } from 'ng-zorro-antd/icon';
10+
import { NzSpinModule } from 'ng-zorro-antd/spin';
11+
12+
import { CrossOrigin, Excavation, Modules } from './typing';
13+
import { DEFAULT_BACKGROUND_COLOR, DEFAULT_FRONT_COLOR, excavateModules, generatePath, isSupportPath2d } from './utils';
14+
15+
@Component({
16+
changeDetection: ChangeDetectionStrategy.OnPush,
17+
selector: 'nz-qrcode-canvas',
18+
exportAs: 'nzQRCodeCanvas',
19+
template: `
20+
<canvas role="img" #canvas></canvas>
21+
@if (icon()) {
22+
<img style="display:none;" #image alt="QR-Code" [attr.src]="this.icon()" crossorigin="anonymous" />
23+
}
24+
`,
25+
styles: `
26+
:host {
27+
display: block;
28+
line-height: 0;
29+
}
30+
`,
31+
imports: [NzSpinModule, NzButtonModule, NzIconModule]
32+
})
33+
export class NzQrcodeCanvasComponent implements AfterViewInit {
34+
@ViewChild('canvas', { static: false }) canvas!: ElementRef<HTMLCanvasElement>;
35+
@ViewChild('image', { static: false }) image!: ElementRef<HTMLImageElement>;
36+
readonly icon = input<string>('');
37+
readonly margin = input<number>(0);
38+
readonly cells = input<Modules>([]);
39+
readonly numCells = input<number>(0);
40+
readonly calculatedImageSettings = input<{
41+
x: number;
42+
y: number;
43+
h: number;
44+
w: number;
45+
excavation: Excavation | null;
46+
opacity: number;
47+
crossOrigin: CrossOrigin;
48+
} | null>(null);
49+
readonly size = input<number>(160);
50+
readonly color = input<string>(DEFAULT_FRONT_COLOR);
51+
readonly bgColor = input<string>(DEFAULT_BACKGROUND_COLOR);
52+
53+
constructor() {
54+
effect(() => {
55+
this.icon();
56+
this.margin();
57+
this.cells();
58+
this.numCells();
59+
this.calculatedImageSettings();
60+
this.size();
61+
this.color();
62+
this.bgColor();
63+
if (!this.canvas?.nativeElement) {
64+
return;
65+
}
66+
67+
this.render();
68+
});
69+
}
70+
71+
ngAfterViewInit(): void {
72+
this.render();
73+
}
74+
75+
private render(): void {
76+
const ctx = this.canvas.nativeElement.getContext('2d') as CanvasRenderingContext2D;
77+
if (!ctx) {
78+
return;
79+
}
80+
81+
this.setupCanvas(ctx);
82+
this.drawQRCode(ctx);
83+
this.handleImageLoading(ctx);
84+
}
85+
86+
private setupCanvas(ctx: CanvasRenderingContext2D): void {
87+
const pixelRatio = window.devicePixelRatio || 1;
88+
this.canvas.nativeElement.height = this.canvas.nativeElement.width = this.size() * pixelRatio;
89+
this.canvas.nativeElement.style.width = this.canvas.nativeElement.style.height = `${this.size()}px`;
90+
91+
const scale = (this.size() / this.numCells()) * pixelRatio;
92+
ctx.scale(scale, scale);
93+
94+
ctx.fillStyle = this.bgColor();
95+
ctx.fillRect(0, 0, this.numCells(), this.numCells());
96+
ctx.fillStyle = this.color();
97+
}
98+
99+
private drawQRCode(ctx: CanvasRenderingContext2D): void {
100+
const cellsToDraw = this.getCellsToDraw();
101+
const haveImageToRender = this.haveImageToRender();
102+
103+
if (!haveImageToRender) {
104+
this.renderQRCode(ctx, cellsToDraw);
105+
}
106+
}
107+
108+
private getCellsToDraw(): Modules {
109+
let cellsToDraw = this.cells();
110+
const imageSettings = this.calculatedImageSettings();
111+
if (this.haveImageToRender() && imageSettings && imageSettings.excavation) {
112+
cellsToDraw = excavateModules(this.cells(), imageSettings.excavation);
113+
}
114+
return cellsToDraw;
115+
}
116+
117+
private haveImageToRender(): boolean {
118+
return this.calculatedImageSettings != null && !!this.image;
119+
}
120+
121+
private renderQRCode(ctx: CanvasRenderingContext2D, cells: Modules): void {
122+
if (isSupportPath2d) {
123+
ctx.fill(new Path2D(generatePath(cells, this.margin())));
124+
} else {
125+
cells.forEach((row, rdx) => {
126+
row.forEach((cell, cdx) => {
127+
if (cell) {
128+
ctx.fillRect(cdx + this.margin(), rdx + this.margin(), 1, 1);
129+
}
130+
});
131+
});
132+
}
133+
}
134+
135+
private handleImageLoading(ctx: CanvasRenderingContext2D): void {
136+
if (!this.haveImageToRender()) {
137+
return;
138+
}
139+
140+
const onLoad = (): void => {
141+
this.cleanupImageListeners(onLoad, onError);
142+
this.onImageLoadSuccess(ctx);
143+
};
144+
145+
const onError = (): void => {
146+
this.cleanupImageListeners(onLoad, onError);
147+
this.onImageLoadError(ctx);
148+
};
149+
150+
this.image.nativeElement.addEventListener('load', onLoad);
151+
this.image.nativeElement.addEventListener('error', onError);
152+
}
153+
154+
private onImageLoadSuccess(ctx: CanvasRenderingContext2D): void {
155+
const cellsToDraw = this.getCellsToDraw();
156+
this.renderQRCode(ctx, cellsToDraw);
157+
158+
const imageSettings = this.calculatedImageSettings();
159+
if (imageSettings) {
160+
ctx.globalAlpha = imageSettings.opacity;
161+
ctx.drawImage(
162+
this.image.nativeElement,
163+
imageSettings.x + this.margin(),
164+
imageSettings.y + this.margin(),
165+
imageSettings.w,
166+
imageSettings.h
167+
);
168+
}
169+
}
170+
171+
private onImageLoadError(ctx: CanvasRenderingContext2D): void {
172+
const cellsToDraw = this.getCellsToDraw();
173+
this.renderQRCode(ctx, cellsToDraw);
174+
}
175+
176+
private cleanupImageListeners(onLoad: () => void, onError: () => void): void {
177+
if (this.image?.nativeElement) {
178+
this.image.nativeElement.removeEventListener('load', onLoad);
179+
this.image.nativeElement.removeEventListener('error', onError);
180+
}
181+
}
182+
}

0 commit comments

Comments
 (0)