2025-09-18 23:33:06 +08:00
|
|
|
import { Sprite, SpriteFrame, Texture2D, ImageAsset, Node } from 'cc';
|
|
|
|
|
import qrcode from 'qrcode-generator';
|
2025-09-21 20:36:19 +08:00
|
|
|
import { logger } from "db://assets/Scripts/Main/Common/Logger";
|
2025-09-18 23:33:06 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 二维码生成工具类
|
|
|
|
|
* 提供生成二维码Sprite的静态方法
|
|
|
|
|
*/
|
|
|
|
|
export class QRCodeGenerator {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成二维码 Sprite 组件(异步版本)
|
|
|
|
|
* @param text 要生成二维码的文本内容
|
|
|
|
|
* @param size 二维码尺寸,默认256x256
|
|
|
|
|
* @param margin 二维码边距,默认4
|
|
|
|
|
* @param callback 完成回调函数
|
|
|
|
|
*/
|
|
|
|
|
public static generateQRCodeSprite(text: string, size: number = 256, margin: number = 4, callback?: (sprite: Sprite | null) => void): void {
|
|
|
|
|
try {
|
|
|
|
|
if (!text || text.trim().length === 0) {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 文本内容不能为空');
|
2025-09-18 23:33:06 +08:00
|
|
|
callback && callback(null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.generateQRCodeSpriteFrame(text, size, margin, (spriteFrame) => {
|
|
|
|
|
if (!spriteFrame) {
|
|
|
|
|
callback && callback(null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 创建新节点和Sprite组件
|
|
|
|
|
const node = new Node('QRCode');
|
|
|
|
|
const sprite = node.addComponent(Sprite);
|
|
|
|
|
sprite.spriteFrame = spriteFrame;
|
|
|
|
|
|
|
|
|
|
callback && callback(sprite);
|
|
|
|
|
} catch (error) {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 创建Sprite组件失败', error);
|
2025-09-18 23:33:06 +08:00
|
|
|
callback && callback(null);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 生成二维码Sprite失败', error);
|
2025-09-18 23:33:06 +08:00
|
|
|
callback && callback(null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成二维码 SpriteFrame(异步版本)
|
|
|
|
|
* @param text 要生成二维码的文本内容
|
|
|
|
|
* @param size 二维码尺寸,默认256x256
|
|
|
|
|
* @param margin 二维码边距,默认4
|
|
|
|
|
* @param callback 完成回调函数
|
|
|
|
|
*/
|
|
|
|
|
public static generateQRCodeSpriteFrame(text: string, size: number = 256, margin: number = 4, callback?: (spriteFrame: SpriteFrame | null) => void): void {
|
|
|
|
|
try {
|
|
|
|
|
if (!text || text.trim().length === 0) {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 文本内容不能为空');
|
2025-09-18 23:33:06 +08:00
|
|
|
callback && callback(null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (size <= 0 || size > 2048) {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 尺寸必须在1-2048之间');
|
2025-09-18 23:33:06 +08:00
|
|
|
callback && callback(null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成二维码
|
|
|
|
|
const qr = qrcode(0, 'M'); // 0表示自动类型选择,'M'表示中等纠错级别
|
|
|
|
|
qr.addData(text);
|
|
|
|
|
qr.make();
|
|
|
|
|
|
|
|
|
|
// 创建Canvas绘制二维码
|
|
|
|
|
const moduleCount = qr.getModuleCount();
|
|
|
|
|
const cellSize = Math.floor((size - 2 * margin) / moduleCount);
|
|
|
|
|
const actualSize = cellSize * moduleCount + 2 * margin;
|
|
|
|
|
|
|
|
|
|
const canvas = document.createElement('canvas');
|
|
|
|
|
canvas.width = actualSize;
|
|
|
|
|
canvas.height = actualSize;
|
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
|
|
|
|
|
|
if (!ctx) {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 无法获取Canvas上下文');
|
2025-09-18 23:33:06 +08:00
|
|
|
callback && callback(null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 绘制白色背景
|
|
|
|
|
ctx.fillStyle = '#ffffff';
|
|
|
|
|
ctx.fillRect(0, 0, actualSize, actualSize);
|
|
|
|
|
|
|
|
|
|
// 绘制二维码
|
|
|
|
|
ctx.fillStyle = '#000000';
|
|
|
|
|
for (let row = 0; row < moduleCount; row++) {
|
|
|
|
|
for (let col = 0; col < moduleCount; col++) {
|
|
|
|
|
if (qr.isDark(row, col)) {
|
|
|
|
|
ctx.fillRect(
|
|
|
|
|
margin + col * cellSize,
|
|
|
|
|
margin + row * cellSize,
|
|
|
|
|
cellSize,
|
|
|
|
|
cellSize
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用异步方式处理图片创建
|
|
|
|
|
const img = new Image();
|
|
|
|
|
img.onload = () => {
|
|
|
|
|
try {
|
|
|
|
|
// 创建ImageAsset并直接设置图片数据
|
|
|
|
|
const imageAsset = new ImageAsset(img);
|
|
|
|
|
|
|
|
|
|
// 创建Texture2D并设置ImageAsset
|
|
|
|
|
const texture = new Texture2D();
|
|
|
|
|
texture.image = imageAsset;
|
|
|
|
|
|
|
|
|
|
// 创建SpriteFrame并设置纹理
|
|
|
|
|
const spriteFrame = new SpriteFrame();
|
|
|
|
|
spriteFrame.texture = texture;
|
|
|
|
|
|
|
|
|
|
callback && callback(spriteFrame);
|
|
|
|
|
} catch (error) {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 创建纹理失败', error);
|
2025-09-18 23:33:06 +08:00
|
|
|
callback && callback(null);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
img.onerror = () => {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 图片加载失败');
|
2025-09-18 23:33:06 +08:00
|
|
|
callback && callback(null);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 设置图片源,触发加载
|
|
|
|
|
img.src = canvas.toDataURL('image/png');
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 生成二维码SpriteFrame失败', error);
|
2025-09-18 23:33:06 +08:00
|
|
|
callback && callback(null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成二维码的Base64字符串
|
|
|
|
|
* @param text 要生成二维码的文本内容
|
|
|
|
|
* @param size 二维码尺寸,默认256x256
|
|
|
|
|
* @param margin 二维码边距,默认4
|
|
|
|
|
* @returns 二维码的Base64字符串,失败时返回空字符串
|
|
|
|
|
*/
|
|
|
|
|
public static generateQRCodeBase64(text: string, size: number = 256, margin: number = 4): string {
|
|
|
|
|
try {
|
|
|
|
|
if (!text || text.trim().length === 0) {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 文本内容不能为空');
|
2025-09-18 23:33:06 +08:00
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成二维码
|
|
|
|
|
const qr = qrcode(0, 'M');
|
|
|
|
|
qr.addData(text);
|
|
|
|
|
qr.make();
|
|
|
|
|
|
|
|
|
|
// 创建Canvas绘制二维码
|
|
|
|
|
const moduleCount = qr.getModuleCount();
|
|
|
|
|
const cellSize = Math.floor((size - 2 * margin) / moduleCount);
|
|
|
|
|
const actualSize = cellSize * moduleCount + 2 * margin;
|
|
|
|
|
|
|
|
|
|
const canvas = document.createElement('canvas');
|
|
|
|
|
canvas.width = actualSize;
|
|
|
|
|
canvas.height = actualSize;
|
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
|
|
|
|
|
|
if (!ctx) {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 无法获取Canvas上下文');
|
2025-09-18 23:33:06 +08:00
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 绘制白色背景
|
|
|
|
|
ctx.fillStyle = '#ffffff';
|
|
|
|
|
ctx.fillRect(0, 0, actualSize, actualSize);
|
|
|
|
|
|
|
|
|
|
// 绘制二维码
|
|
|
|
|
ctx.fillStyle = '#000000';
|
|
|
|
|
for (let row = 0; row < moduleCount; row++) {
|
|
|
|
|
for (let col = 0; col < moduleCount; col++) {
|
|
|
|
|
if (qr.isDark(row, col)) {
|
|
|
|
|
ctx.fillRect(
|
|
|
|
|
margin + col * cellSize,
|
|
|
|
|
margin + row * cellSize,
|
|
|
|
|
cellSize,
|
|
|
|
|
cellSize
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 返回Base64字符串
|
|
|
|
|
return canvas.toDataURL('image/png');
|
|
|
|
|
} catch (error) {
|
2025-09-21 20:36:19 +08:00
|
|
|
logger.error('QRCodeGenerator: 生成二维码Base64失败', error);
|
2025-09-18 23:33:06 +08:00
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|