feat(工具箱/二维码解析和生成): 添加生成二维码功能
This commit is contained in:
@@ -4,6 +4,15 @@ import {
|
||||
writeBarcode,
|
||||
} from 'zxing-wasm/full';
|
||||
|
||||
/** 默认背景颜色 */
|
||||
const DEFAULT_BGC = 'transparent';
|
||||
|
||||
/** 默认前景颜色 */
|
||||
const DEFAULT_FGC = '#000000';
|
||||
|
||||
/** 模块名称 */
|
||||
const PREFIX = '[qr-code]';
|
||||
|
||||
/**
|
||||
* @desc 二维码读取配置选项
|
||||
* @type { import('zxing-wasm').ReaderOptions }
|
||||
@@ -25,7 +34,7 @@ const readerOptions = {
|
||||
const writerOptions = {
|
||||
ecLevel: '',
|
||||
format: 'QRCode',
|
||||
scale: 1,
|
||||
scale: 0,
|
||||
};
|
||||
|
||||
// 配置 wasm 文件路径
|
||||
@@ -46,7 +55,7 @@ prepareZXingModule({
|
||||
* @param {Blob} blob
|
||||
* @param {Callback} callback
|
||||
*/
|
||||
export function blobToDataURL(blob, callback) {
|
||||
function blobToDataURL(blob, callback) {
|
||||
|
||||
/** @typedef {(data: { error: boolean, result: string }) => void} Callback */
|
||||
|
||||
@@ -70,11 +79,77 @@ export function blobToDataURL(blob, callback) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 修改 SVG 内容,获取信息
|
||||
* @param {object} options
|
||||
* @param {string} options.background
|
||||
* @param {string} options.foreground
|
||||
* @param {string} options.svgString
|
||||
*/
|
||||
function modifySvgContent(options) {
|
||||
|
||||
let {
|
||||
background = DEFAULT_BGC,
|
||||
foreground = DEFAULT_FGC,
|
||||
svgString,
|
||||
} = options;
|
||||
|
||||
let divElement = document.createElement('div');
|
||||
let gElement = null;
|
||||
let pathElement = null;
|
||||
let rectElement = null;
|
||||
let svgElement = null;
|
||||
|
||||
// 添加 DOM 元素,用于获取位置大小信息
|
||||
document.body.appendChild(divElement);
|
||||
|
||||
divElement.innerHTML = svgString;
|
||||
gElement = divElement.getElementsByTagName('g')[0] || null;
|
||||
pathElement = divElement.getElementsByTagName('path')[0] || null;
|
||||
rectElement = divElement.getElementsByTagName('rect')[0] || null;
|
||||
svgElement = divElement.getElementsByTagName('svg')[0] || null;
|
||||
|
||||
if (!(gElement && pathElement && rectElement && svgElement)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 修改填充颜色
|
||||
gElement.setAttribute('fill', foreground);
|
||||
rectElement.setAttribute('fill', background);
|
||||
|
||||
// 获取位置大小信息
|
||||
let rectOfG = gElement.getBoundingClientRect();
|
||||
let rectOfPath = pathElement.getBoundingClientRect();
|
||||
|
||||
let offsetX = Math.round(rectOfPath.left - rectOfG.left);
|
||||
let offsetY = Math.round(rectOfPath.top - rectOfG.top);
|
||||
let sizeW = Math.round(rectOfPath.width);
|
||||
let sizeH = Math.round(rectOfPath.height);
|
||||
let result = {
|
||||
offsetX: offsetX,
|
||||
offsetY: offsetY,
|
||||
sizeW: sizeW,
|
||||
sizeH: sizeH,
|
||||
svgString: divElement.innerHTML,
|
||||
};
|
||||
|
||||
// 输出处理结果
|
||||
console.debug(PREFIX, '处理 SVG', result);
|
||||
|
||||
// 处理完成,移除 DOM 元素
|
||||
document.body.removeChild(divElement);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 将 SVG 字符串渲染到 Canvas
|
||||
* @param {object} options
|
||||
* @param {HTMLCanvasElement} options.canvas
|
||||
* @param {string} options.svgString
|
||||
* @param {string} options.background
|
||||
* @param {string} options.foreground
|
||||
* @param {number} options.drawLeft
|
||||
* @param {number} options.drawTop
|
||||
* @param {number} options.drawWidth
|
||||
@@ -85,27 +160,42 @@ function renderSvgToCanvas(options) {
|
||||
|
||||
let {
|
||||
canvas, svgString,
|
||||
background = DEFAULT_BGC, foreground = DEFAULT_FGC,
|
||||
drawLeft = 0, drawTop = 0,
|
||||
drawWidth = 0, drawHeight = 0,
|
||||
} = options;
|
||||
|
||||
let svgInfo = modifySvgContent({
|
||||
background: background,
|
||||
foreground: foreground,
|
||||
svgString: svgString,
|
||||
});
|
||||
|
||||
if (!svgInfo) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
|
||||
let svgBlob = new Blob([svgString], {
|
||||
let svgBlob = new Blob([svgInfo.svgString], {
|
||||
type: 'image/svg+xml;charset=utf-8',
|
||||
});
|
||||
let svgUrl = URL.createObjectURL(svgBlob);
|
||||
let image = new Image();
|
||||
|
||||
image.onerror = () => {
|
||||
console.error('加载 SVG 失败');
|
||||
console.error(PREFIX, '加载 SVG 失败');
|
||||
URL.revokeObjectURL(svgUrl);
|
||||
resolve(false);
|
||||
};
|
||||
|
||||
image.onload = () => {
|
||||
let ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(image, drawLeft, drawTop, drawWidth, drawHeight);
|
||||
ctx.drawImage(
|
||||
image,
|
||||
svgInfo.offsetX, svgInfo.offsetY, svgInfo.sizeW, svgInfo.sizeH,
|
||||
drawLeft, drawTop, drawWidth, drawHeight,
|
||||
);
|
||||
URL.revokeObjectURL(svgUrl);
|
||||
resolve(true);
|
||||
};
|
||||
@@ -138,7 +228,7 @@ export function readQrCodeImage(image) {
|
||||
|
||||
// 处理读取异常
|
||||
fileReader.onerror = function () {
|
||||
console.error('解析二维码失败:读取图片失败');
|
||||
console.error(PREFIX, '解析二维码失败:读取图片失败');
|
||||
returns.error = '读取图片失败';
|
||||
resolve('');
|
||||
};
|
||||
@@ -163,10 +253,10 @@ export function readQrCodeImage(image) {
|
||||
let textList = returns.textList;
|
||||
|
||||
if (resultList.length === 0) {
|
||||
console.warn('解析二维码失败:未识别到内容');
|
||||
console.warn(PREFIX, '解析二维码失败:未识别到内容');
|
||||
return returns;
|
||||
} else {
|
||||
console.debug('解析二维码成功:', resultList);
|
||||
console.debug(PREFIX, '解析二维码成功:', resultList);
|
||||
}
|
||||
|
||||
for (let i = 0; i < resultList.length; i++) {
|
||||
@@ -180,7 +270,7 @@ export function readQrCodeImage(image) {
|
||||
return returns;
|
||||
|
||||
}).catch((error) => {
|
||||
console.error('解析二维码失败:');
|
||||
console.error(PREFIX, '解析二维码失败:');
|
||||
console.error(error);
|
||||
returns.error = String(error);
|
||||
return returns;
|
||||
@@ -192,13 +282,19 @@ export function readQrCodeImage(image) {
|
||||
* @description 生成二维码图片
|
||||
* @param {object} options
|
||||
* @param {string} options.content
|
||||
* @param {string} options.background
|
||||
* @param {string} options.foreground
|
||||
* @param {number} options.width
|
||||
* @param {number} options.height
|
||||
* @returns 二维码图片 DataURL
|
||||
*/
|
||||
export function writeQrCodeImage(options = {}) {
|
||||
|
||||
let { content = '', width = 256, height = 256 } = options;
|
||||
let {
|
||||
content = '',
|
||||
background = DEFAULT_BGC, foreground = DEFAULT_FGC,
|
||||
width = 256, height = 256,
|
||||
} = options;
|
||||
|
||||
let canvas = document.createElement('canvas');
|
||||
let ctx = canvas.getContext('2d');
|
||||
@@ -208,15 +304,15 @@ export function writeQrCodeImage(options = {}) {
|
||||
canvas.height = height;
|
||||
|
||||
// 设置背景颜色
|
||||
ctx.fillStyle = '#FFFFFF';
|
||||
ctx.fillStyle = background;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
return writeBarcode(content, writerOptions).then((result) => {
|
||||
|
||||
console.debug('生成二维码', result);
|
||||
console.debug(PREFIX, '生成二维码', result);
|
||||
|
||||
if (result.error) {
|
||||
console.error(`生成二维码失败:${result.error}`);
|
||||
console.error(PREFIX, `生成二维码失败:${result.error}`);
|
||||
return '';
|
||||
} else {
|
||||
return result.svg;
|
||||
@@ -227,6 +323,8 @@ export function writeQrCodeImage(options = {}) {
|
||||
return renderSvgToCanvas({
|
||||
canvas: canvas,
|
||||
svgString: svgString,
|
||||
background: background,
|
||||
foreground: foreground,
|
||||
drawLeft: 0,
|
||||
drawTop: 0,
|
||||
drawWidth: width,
|
||||
@@ -244,7 +342,7 @@ export function writeQrCodeImage(options = {}) {
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
console.error('生成二维码失败:');
|
||||
console.error(PREFIX, '生成二维码失败:');
|
||||
console.error(error);
|
||||
return '';
|
||||
});
|
||||
|
Reference in New Issue
Block a user