|
|
// main.js
|
|
|
|
|
|
// 当整个页面的HTML内容加载完成后,执行此函数
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
|
// 获取页面上的关键元素
|
|
|
const textInput = document.getElementById('text-input');
|
|
|
const generateBtn = document.getElementById('generate-btn');
|
|
|
const qrcodeContainer = document.getElementById('qrcode-container');
|
|
|
|
|
|
// 为“生成二维码”按钮添加点击事件监听器
|
|
|
generateBtn.addEventListener('click', () => {
|
|
|
// 获取输入框中的文本,并用 trim() 清除前后的空白字符
|
|
|
const text = textInput.value.trim();
|
|
|
|
|
|
// 在生成新的二维码之前,清空之前可能存在的旧二维码
|
|
|
qrcodeContainer.innerHTML = '';
|
|
|
|
|
|
// 如果文本为空,则不执行任何操作
|
|
|
if (!text) {
|
|
|
console.log('输入内容为空,已中止生成。');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// --- 智能分割逻辑 ---
|
|
|
// 我们使用一个固定的字节数上限。这是二维码国标(GB/T 18284-2000)规定的,
|
|
|
// 在版本40、纠错等级H(最高)的情况下,最大可容纳的字节数。
|
|
|
const MAX_BYTES_PER_QR = 1273;
|
|
|
const textEncoder = new TextEncoder(); // 用于计算字符串的UTF-8字节长度
|
|
|
const chunks = [];
|
|
|
let currentPos = 0;
|
|
|
|
|
|
while (currentPos < text.length) {
|
|
|
// 使用二分查找来高效地找到当前位置后,能容纳在单个二维码中的最长子字符串
|
|
|
let low = currentPos;
|
|
|
let high = text.length;
|
|
|
let bestEnd = currentPos;
|
|
|
|
|
|
while (low <= high) {
|
|
|
const mid = Math.floor((low + high) / 2);
|
|
|
|
|
|
// mid不能停在原点,否则会死循环
|
|
|
if (mid === currentPos) {
|
|
|
low = mid + 1;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
const substring = text.substring(currentPos, mid);
|
|
|
const byteLength = textEncoder.encode(substring).length;
|
|
|
|
|
|
if (byteLength <= MAX_BYTES_PER_QR) {
|
|
|
// 如果当前子字符串的字节长度在限制内,说明它是一个有效的块
|
|
|
// 我们记录下这个有效的终点,并尝试寻找更长的块
|
|
|
bestEnd = mid;
|
|
|
low = mid + 1;
|
|
|
} else {
|
|
|
// 如果超出了字节限制,则需要缩短子字符串的长度
|
|
|
high = mid - 1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 处理极端情况:如果单个字符的字节数就超过了上限,我们也只能把它单独作为一个块
|
|
|
if (bestEnd === currentPos && currentPos < text.length) {
|
|
|
bestEnd = currentPos + 1;
|
|
|
}
|
|
|
|
|
|
// 将找到的最佳块添加到数组中
|
|
|
chunks.push(text.substring(currentPos, bestEnd));
|
|
|
// 从新的位置继续寻找下一个块
|
|
|
currentPos = bestEnd;
|
|
|
}
|
|
|
|
|
|
const totalChunks = chunks.length;
|
|
|
console.log(`总字符数: ${text.length},通过智能分割,将生成 ${totalChunks} 个二维码。`);
|
|
|
|
|
|
// --- 循环生成二维码 ---
|
|
|
chunks.forEach((chunk, i) => {
|
|
|
// --- 创建包裹单个二维码及其标签的容器 ---
|
|
|
const itemDiv = document.createElement('div');
|
|
|
itemDiv.className = 'qrcode-item';
|
|
|
|
|
|
// --- 创建并添加序号标签 ---
|
|
|
const label = document.createElement('p');
|
|
|
label.className = 'qrcode-label';
|
|
|
label.textContent = totalChunks > 1 ? `二维码 ${i + 1}/${totalChunks}` : '二维码';
|
|
|
itemDiv.appendChild(label);
|
|
|
|
|
|
// --- 创建用于绘制二维码的 <canvas> 元素 ---
|
|
|
const canvas = document.createElement('canvas');
|
|
|
itemDiv.appendChild(canvas);
|
|
|
|
|
|
qrcodeContainer.appendChild(itemDiv);
|
|
|
|
|
|
// --- 使用 QRCode.toCanvas 方法将二维码绘制到指定的 canvas 上 ---
|
|
|
QRCode.toCanvas(
|
|
|
canvas,
|
|
|
chunk,
|
|
|
{
|
|
|
margin: 2, // 二维码边距
|
|
|
color: {
|
|
|
dark: '#000000', // 二维码深色部分
|
|
|
light: '#ffffff', // 二维码浅色部分
|
|
|
},
|
|
|
errorCorrectionLevel: 'H', // 纠错级别: 'L', 'M', 'Q', 'H'
|
|
|
},
|
|
|
function (error) {
|
|
|
if (error) {
|
|
|
console.error(`为第 ${i + 1} 块内容生成二维码时出错:`, error);
|
|
|
// 如果生成失败,在 canvas 上显示错误信息
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
if (ctx) {
|
|
|
// 设置一个基础尺寸,以防canvas没有宽高
|
|
|
canvas.width = 400;
|
|
|
canvas.height = 400;
|
|
|
ctx.font = '20px Arial';
|
|
|
ctx.fillStyle = 'red';
|
|
|
ctx.textAlign = 'center';
|
|
|
ctx.textBaseline = 'middle';
|
|
|
ctx.fillText('二维码生成失败', canvas.width / 2, canvas.height / 2);
|
|
|
}
|
|
|
} else {
|
|
|
console.log(`已成功为第 ${i + 1} 块内容生成二维码。`);
|
|
|
}
|
|
|
}
|
|
|
);
|
|
|
});
|
|
|
});
|
|
|
});
|