You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
6.9 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// decode.js
// This file will contain the logic for the QR code decoding feature.
document.addEventListener('DOMContentLoaded', () => {
console.log('解码页面脚本已加载。');
// 获取页面上的关键元素
const qrInput = document.getElementById('qr-input');
const imagePreviewContainer = document.getElementById('image-preview-container');
const decodeBtn = document.getElementById('decode-btn');
const resultText = document.getElementById('result-text');
const copyBtn = document.getElementById('copy-btn');
// 初始化 SortableJS为预览容器开启拖拽排序功能
new Sortable(imagePreviewContainer, {
animation: 150, // 拖拽动画的毫秒数
ghostClass: 'sortable-ghost', // 拖拽时占位元素的类名
});
// 监听文件输入框的 change 事件
qrInput.addEventListener('change', handleFileSelect);
/**
* 处理用户选择的文件
* @param {Event} event - input change 事件对象
*/
function handleFileSelect(event) {
// 获取用户选择的文件列表
const files = event.target.files;
if (!files) {
return;
}
// 遍历所有选择的文件
for (const file of files) {
// 确保文件是图片类型
if (!file.type.startsWith('image/')) {
continue;
}
const reader = new FileReader();
// 文件读取成功后的回调
reader.onload = function (e) {
createPreview(e.target.result, file.name);
};
// 以 Data URL 的格式读取文件
reader.readAsDataURL(file);
}
// 清空 input 的值,这样即使用户连续选择相同的文件也能触发 change 事件
qrInput.value = '';
}
/**
* 创建并显示单个图片预览
* @param {string} src - 图片的 Data URL
* @param {string} name - 图片的文件名
*/
function createPreview(src, name) {
// 创建预览项的容器
const previewItem = document.createElement('div');
previewItem.className = 'preview-item';
previewItem.title = name; // 鼠标悬停时显示文件名
// 创建图片元素
const img = document.createElement('img');
img.src = src;
img.alt = name;
// 创建移除按钮
const removeBtn = document.createElement('button');
removeBtn.className = 'remove-btn';
removeBtn.textContent = '×';
removeBtn.title = '移除此图片';
// 为移除按钮添加点击事件
removeBtn.addEventListener('click', (e) => {
e.stopPropagation(); // 防止触发其他事件
previewItem.remove(); // 从 DOM 中移除预览项
});
// 将图片和移除按钮添加到预览项容器中
previewItem.appendChild(img);
previewItem.appendChild(removeBtn);
// 将预览项添加到主预览容器中
imagePreviewContainer.appendChild(previewItem);
}
// 为解码按钮添加点击事件
decodeBtn.addEventListener('click', () => {
// 清空之前的结果
resultText.value = '';
// 获取所有预览项中的图片元素,它们的顺序就是用户拖拽后的顺序
const images = imagePreviewContainer.querySelectorAll('.preview-item img');
if (images.length === 0) {
resultText.value = '请先上传至少一张二维码图片。';
return;
}
const decodedParts = [];
let completed = 0;
// 遍历所有图片进行解码
images.forEach((img, index) => {
const decodedText = decodeImage(img);
decodedParts[index] = decodedText;
completed++;
// 当所有图片都处理完毕后,拼接结果
if (completed === images.length) {
resultText.value = decodedParts.join('');
}
});
});
/**
* 解码单个图片元素
* @param {HTMLImageElement} img - 要解码的图片元素
* @returns {string} - 解码后的文本,或者一个错误提示
*/
function decodeImage(img) {
// 创建一个临时的、离屏的 canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// --- 图片缩放逻辑 ---
// 定义一个用于处理的最大尺寸,过大的图片会导致 jsQR 性能下降或失败
const MAX_DIMENSION = 1000;
let { naturalWidth: width, naturalHeight: height } = img;
// 如果图片尺寸过大,则按比例缩小
if (width > MAX_DIMENSION || height > MAX_DIMENSION) {
if (width > height) {
height = Math.round(height * (MAX_DIMENSION / width));
width = MAX_DIMENSION;
} else {
width = Math.round(width * (MAX_DIMENSION / height));
height = MAX_DIMENSION;
}
}
// 设置 canvas 的尺寸为计算后的尺寸
canvas.width = width;
canvas.height = height;
// 将图片(可能会被缩放)绘制到 canvas 上
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// 从 canvas 获取图像数据
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
try {
// 使用 jsQR 库解码
const code = jsQR(imageData.data, imageData.width, imageData.height);
if (code && code.data) {
// 解码成功,返回文本内容
return code.data;
} else {
// 未找到二维码
return `[图片 ${img.alt} 未能识别出二维码]`;
}
} catch (error) {
console.error(`解码图片 ${img.alt} 时发生错误:`, error);
return `[图片 ${img.alt} 解码失败]`;
}
}
// 为复制按钮添加点击事件
copyBtn.addEventListener('click', () => {
const textToCopy = resultText.value;
if (!textToCopy) {
return;
}
navigator.clipboard
.writeText(textToCopy)
.then(() => {
// 复制成功后的用户反馈
const originalText = copyBtn.textContent;
copyBtn.textContent = '已复制!';
copyBtn.disabled = true;
setTimeout(() => {
copyBtn.textContent = originalText;
copyBtn.disabled = false;
}, 2000);
})
.catch((err) => {
console.error('复制文本失败:', err);
// 可以选择在这里给用户一个错误提示
alert('复制失败,您的浏览器可能不支持或未授权剪贴板操作。');
});
});
});