// 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('复制失败,您的浏览器可能不支持或未授权剪贴板操作。'); }); }); });