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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/qr-code/demo/color.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 6
order: 7
title:
zh-CN: 自定义颜色
en-US: Custom Color
Expand Down
2 changes: 1 addition & 1 deletion components/qr-code/demo/padding.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 5
order: 6
title:
zh-CN: 带衬垫
en-US: With padding
Expand Down
5 changes: 3 additions & 2 deletions components/qr-code/demo/padding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import { NzQRCodeModule } from 'ng-zorro-antd/qr-code';
selector: 'nz-demo-qr-code-padding',
imports: [NzQRCodeModule],
template: `
<nz-qrcode [nzPadding]="12" nzValue="https://ng.ant.design/"></nz-qrcode>
<nz-qrcode [nzPadding]="[12, 24]" nzValue="https://ng.ant.design/"></nz-qrcode>
<nz-qrcode [nzPadding]="2" nzValue="https://ng.ant.design/"></nz-qrcode>
<nz-qrcode nzType="svg" [nzPadding]="2" nzValue="https://ng.ant.design/"></nz-qrcode>
`,
styles: [
`
nz-qrcode {
margin-right: 12px;
padding: 0;
}
`
]
Expand Down
15 changes: 15 additions & 0 deletions components/qr-code/demo/type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
order: 5
version: 21.0.0
title:
zh-CN: 自定义渲染类型
en-US: Custom Render Type
---

## zh-CN

通过设置 `nzType` 自定义渲染结果,提供 `canvas` 和 `svg` 两个选项。

## en-US

Customize the rendering results by `nzType`, provide options `canvas` and `svg`.
20 changes: 20 additions & 0 deletions components/qr-code/demo/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Component } from '@angular/core';

import { NzQRCodeModule } from 'ng-zorro-antd/qr-code';

@Component({
selector: 'nz-demo-qr-code-type',
imports: [NzQRCodeModule],
template: `
<nz-qrcode nzValue="https://ng.ant.design/"></nz-qrcode>
<nz-qrcode nzValue="https://ng.ant.design/" nzType="svg"></nz-qrcode>
`,
styles: [
`
nz-qrcode {
margin-right: 12px;
}
`
]
})
export class NzDemoQrCodeTypeComponent {}
30 changes: 16 additions & 14 deletions components/qr-code/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,22 @@ Used when the text needs to be converted into a QR Code.

### nz-qrcode

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

## Note

Expand Down
30 changes: 16 additions & 14 deletions components/qr-code/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,22 @@ description: 能够将文本转换生成二维码的组件,支持自定义配

### nz-qrcode

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

## 注意

Expand Down
194 changes: 194 additions & 0 deletions components/qr-code/qrcode-canvas.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { AfterViewInit, ChangeDetectionStrategy, Component, effect, ElementRef, input, viewChild } from '@angular/core';

import { CrossOrigin, Excavation, Modules } from './typing';
import { DEFAULT_BACKGROUND_COLOR, DEFAULT_FRONT_COLOR, excavateModules, generatePath, isSupportPath2d } from './utils';

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'nz-qrcode-canvas',
exportAs: 'nzQRCodeCanvas',
template: `
<canvas role="img" #canvas></canvas>
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The canvas element has role="img" but is missing an accessible label. Add an aria-label or allow passing a title input (like the title property defined in QRProps interface in typing.ts) to provide screen readers with meaningful context about the QR code content.

Copilot uses AI. Check for mistakes.
@if (icon()) {
<img style="display:none;" #image alt="QR-Code" [attr.src]="this.icon()" crossorigin="anonymous" />
}
`,
styles: `
:host {
display: block;
line-height: 0;
}
`
})
export class NzQrcodeCanvasComponent implements AfterViewInit {
canvas = viewChild.required<ElementRef<HTMLCanvasElement>>('canvas');
image = viewChild<ElementRef<HTMLImageElement>>('image');
readonly icon = input<string>('');
readonly margin = input<number>(0);
readonly cells = input<Modules>([]);
readonly numCells = input<number>(0);
readonly calculatedImageSettings = input<{
x: number;
y: number;
h: number;
w: number;
excavation: Excavation | null;
opacity: number;
crossOrigin: CrossOrigin;
} | null>(null);
readonly size = input<number>(160);
readonly color = input<string>(DEFAULT_FRONT_COLOR);
readonly bgColor = input<string>(DEFAULT_BACKGROUND_COLOR);

constructor() {
effect(() => {
this.icon();
this.margin();
this.cells();
this.numCells();
this.calculatedImageSettings();
this.size();
this.color();
this.bgColor();
if (!this.canvas()?.nativeElement) {
return;
}

this.render();
});
}

ngAfterViewInit(): void {
this.render();
}

private render(): void {
const canvas = this.canvas();
if (!canvas) {
return;
}

const ctx = canvas.nativeElement.getContext('2d') as CanvasRenderingContext2D;
if (!ctx) {
return;
}

this.setupCanvas(ctx);
this.drawQRCode(ctx);
this.handleImageLoading(ctx);
}

private setupCanvas(ctx: CanvasRenderingContext2D): void {
const canvas = this.canvas();
if (!canvas) {
return;
}

const pixelRatio = window.devicePixelRatio || 1;
canvas.nativeElement.height = canvas.nativeElement.width = this.size() * pixelRatio;
canvas.nativeElement.style.width = canvas.nativeElement.style.height = `${this.size()}px`;

const scale = (this.size() / this.numCells()) * pixelRatio;
ctx.scale(scale, scale);

ctx.fillStyle = this.bgColor();
ctx.fillRect(0, 0, this.numCells(), this.numCells());
ctx.fillStyle = this.color();
}

private drawQRCode(ctx: CanvasRenderingContext2D): void {
const cellsToDraw = this.getCellsToDraw();
const haveImageToRender = this.haveImageToRender();

if (!haveImageToRender) {
this.renderQRCode(ctx, cellsToDraw);
}
}

private getCellsToDraw(): Modules {
let cellsToDraw = this.cells();
const imageSettings = this.calculatedImageSettings();
if (this.haveImageToRender() && imageSettings && imageSettings.excavation) {
cellsToDraw = excavateModules(this.cells(), imageSettings.excavation);
}
return cellsToDraw;
}

private haveImageToRender(): boolean {
return this.calculatedImageSettings() != null && !!this.image();
}

private renderQRCode(ctx: CanvasRenderingContext2D, cells: Modules): void {
if (isSupportPath2d) {
ctx.fill(new Path2D(generatePath(cells, this.margin())));
} else {
cells.forEach((row, rdx) => {
row.forEach((cell, cdx) => {
if (cell) {
ctx.fillRect(cdx + this.margin(), rdx + this.margin(), 1, 1);
}
});
});
}
}

private handleImageLoading(ctx: CanvasRenderingContext2D): void {
if (!this.haveImageToRender()) {
return;
}

const image = this.image();
if (!image) {
return;
}

const onLoad = (): void => {
this.cleanupImageListeners(onLoad, onError);
this.onImageLoadSuccess(ctx);
};

const onError = (): void => {
this.cleanupImageListeners(onLoad, onError);
this.onImageLoadError(ctx);
};

image.nativeElement.addEventListener('load', onLoad);
image.nativeElement.addEventListener('error', onError);
}

private onImageLoadSuccess(ctx: CanvasRenderingContext2D): void {
const cellsToDraw = this.getCellsToDraw();
this.renderQRCode(ctx, cellsToDraw);

const imageSettings = this.calculatedImageSettings();
const image = this.image();
if (imageSettings && image) {
ctx.globalAlpha = imageSettings.opacity;
ctx.drawImage(
image.nativeElement,
imageSettings.x + this.margin(),
imageSettings.y + this.margin(),
imageSettings.w,
imageSettings.h
);
}
}

private onImageLoadError(ctx: CanvasRenderingContext2D): void {
const cellsToDraw = this.getCellsToDraw();
this.renderQRCode(ctx, cellsToDraw);
}

private cleanupImageListeners(onLoad: () => void, onError: () => void): void {
const image = this.image();
if (image?.nativeElement) {
image.nativeElement.removeEventListener('load', onLoad);
image.nativeElement.removeEventListener('error', onError);
}
}
}
Loading