tauri-android-tax/src/qrscan.js
2026-07-09 20:03:35 +08:00

77 lines
2.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 二维码解码核心逻辑:与 UI、相机、Tauri 解耦,便于单元测试。
// 相机扫码走原生插件,图片扫码走纯 JS 的 jsQR桌面端也可用。
import jsQR from "jsqr";
/**
* 校验扫码内容是否为有效网址(仅接受 http/https
* @param {unknown} content 扫码得到的原始内容。
* @returns {string} 合法网址(去除首尾空白);非法时返回空字符串。
*/
export function extractUrl(content) {
if (typeof content !== "string") return "";
const text = content.trim();
return /^https?:\/\//i.test(text) ? text : "";
}
/**
* 从像素数据中解码二维码。
* @param {{data: Uint8ClampedArray, width: number, height: number}} imageData
* 画布像素数据RGBA
* @returns {string|null} 解码到的文本;未识别到返回 null。
*/
export function decodeQrFromImageData(imageData) {
if (!imageData || !imageData.data || !imageData.width || !imageData.height) {
return null;
}
// attemptBoth 允许识别反色(浅底深码或深底浅码)二维码,提升清晰码的识别率。
const result = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: "attemptBoth",
});
return result ? result.data : null;
}
/**
* 将图片元素绘制到离屏画布并取出像素数据(仅浏览器环境可用)。
* @param {CanvasImageSource & {width:number,height:number}} image 已加载的图片源。
* @param {number} [maxSize=1600] 长边最大像素,过大图片等比缩小以兼顾速度与精度。
* @returns {ImageData} 画布像素数据。
*/
export function imageToImageData(image, maxSize = 1600) {
const sw = image.naturalWidth || image.width;
const sh = image.naturalHeight || image.height;
const scale = Math.min(1, maxSize / Math.max(sw, sh));
const w = Math.max(1, Math.round(sw * scale));
const h = Math.max(1, Math.round(sh * scale));
const canvas = document.createElement("canvas");
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, w, h);
return ctx.getImageData(0, 0, w, h);
}
/**
* 从文件(用户选择的图片)解码二维码(仅浏览器环境可用)。
* @param {Blob} file 图片文件。
* @returns {Promise<string|null>} 解码文本;未识别到返回 null。
*/
export function decodeQrFromFile(file) {
return new Promise((resolve, reject) => {
const url = URL.createObjectURL(file);
const img = new Image();
img.onload = () => {
URL.revokeObjectURL(url);
try {
resolve(decodeQrFromImageData(imageToImageData(img)));
} catch (e) {
reject(e);
}
};
img.onerror = () => {
URL.revokeObjectURL(url);
reject(new Error("图片加载失败"));
};
img.src = url;
});
}