|
|
|
@ -21,21 +21,59 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 定义每个二维码的最大字符数
|
|
|
|
// --- 智能分割逻辑 ---
|
|
|
|
const chunkSize = 1000;
|
|
|
|
// 我们使用一个固定的字节数上限。这是二维码国标(GB/T 18284-2000)规定的,
|
|
|
|
// 计算需要生成多少个二维码
|
|
|
|
// 在版本40、纠错等级H(最高)的情况下,最大可容纳的字节数。
|
|
|
|
const totalChunks = Math.ceil(text.length / chunkSize);
|
|
|
|
const MAX_BYTES_PER_QR = 1273;
|
|
|
|
|
|
|
|
const textEncoder = new TextEncoder(); // 用于计算字符串的UTF-8字节长度
|
|
|
|
|
|
|
|
const chunks = [];
|
|
|
|
|
|
|
|
let currentPos = 0;
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`总字符数: ${text.length},将生成 ${totalChunks} 个二维码。`);
|
|
|
|
while (currentPos < text.length) {
|
|
|
|
|
|
|
|
// 使用二分查找来高效地找到当前位置后,能容纳在单个二维码中的最长子字符串
|
|
|
|
|
|
|
|
let low = currentPos;
|
|
|
|
|
|
|
|
let high = text.length;
|
|
|
|
|
|
|
|
let bestEnd = currentPos;
|
|
|
|
|
|
|
|
|
|
|
|
// 循环处理每一块文本
|
|
|
|
while (low <= high) {
|
|
|
|
for (let i = 0; i < totalChunks; i++) {
|
|
|
|
const mid = Math.floor((low + high) / 2);
|
|
|
|
// 计算当前块的起始和结束位置
|
|
|
|
|
|
|
|
const start = i * chunkSize;
|
|
|
|
|
|
|
|
const end = start + chunkSize;
|
|
|
|
|
|
|
|
// 从总文本中截取当前块的文本
|
|
|
|
|
|
|
|
const chunk = text.substring(start, end);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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');
|
|
|
|
const itemDiv = document.createElement('div');
|
|
|
|
itemDiv.className = 'qrcode-item';
|
|
|
|
itemDiv.className = 'qrcode-item';
|
|
|
|
@ -43,33 +81,47 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
// --- 创建并添加序号标签 ---
|
|
|
|
// --- 创建并添加序号标签 ---
|
|
|
|
const label = document.createElement('p');
|
|
|
|
const label = document.createElement('p');
|
|
|
|
label.className = 'qrcode-label';
|
|
|
|
label.className = 'qrcode-label';
|
|
|
|
// 如果总数大于1,则显示 "二维码 x/y",否则只显示 "二维码"
|
|
|
|
|
|
|
|
label.textContent = totalChunks > 1 ? `二维码 ${i + 1}/${totalChunks}` : '二维码';
|
|
|
|
label.textContent = totalChunks > 1 ? `二维码 ${i + 1}/${totalChunks}` : '二维码';
|
|
|
|
itemDiv.appendChild(label);
|
|
|
|
itemDiv.appendChild(label);
|
|
|
|
|
|
|
|
|
|
|
|
// --- 创建用于生成二维码的占位符元素 ---
|
|
|
|
// --- 创建用于绘制二维码的 <canvas> 元素 ---
|
|
|
|
const qrcodeDiv = document.createElement('div');
|
|
|
|
const canvas = document.createElement('canvas');
|
|
|
|
// 给这个div一个唯一的ID,虽然qrcodejs也可以直接接收元素对象
|
|
|
|
itemDiv.appendChild(canvas);
|
|
|
|
qrcodeDiv.id = `qrcode-${i}`;
|
|
|
|
|
|
|
|
itemDiv.appendChild(qrcodeDiv);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- 将完整的二维码项(标签+占位符)添加到主容器中 ---
|
|
|
|
|
|
|
|
qrcodeContainer.appendChild(itemDiv);
|
|
|
|
qrcodeContainer.appendChild(itemDiv);
|
|
|
|
|
|
|
|
|
|
|
|
// --- 使用 qrcode.js 库生成二维码 ---
|
|
|
|
// --- 使用 QRCode.toCanvas 方法将二维码绘制到指定的 canvas 上 ---
|
|
|
|
// 实例化 QRCode 对象
|
|
|
|
QRCode.toCanvas(
|
|
|
|
// 第一个参数是二维码要渲染到的DOM元素
|
|
|
|
canvas,
|
|
|
|
// 第二个参数是配置对象
|
|
|
|
chunk,
|
|
|
|
new QRCode(qrcodeDiv, {
|
|
|
|
{
|
|
|
|
text: chunk, // 当前块的文本内容
|
|
|
|
margin: 2, // 二维码边距
|
|
|
|
width: 400, // 二维码宽度
|
|
|
|
color: {
|
|
|
|
height: 400, // 二维码高度
|
|
|
|
dark: '#000000', // 二维码深色部分
|
|
|
|
colorDark: '#000000', // 二维码颜色
|
|
|
|
light: '#ffffff', // 二维码浅色部分
|
|
|
|
colorLight: '#ffffff', // 二维码背景色
|
|
|
|
},
|
|
|
|
correctLevel: QRCode.CorrectLevel.H, // 纠错级别,H是最高级
|
|
|
|
errorCorrectionLevel: 'H', // 纠错级别: 'L', 'M', 'Q', 'H'
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
function (error) {
|
|
|
|
console.log(`已为第 ${i + 1} 块内容生成二维码。`);
|
|
|
|
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} 块内容生成二维码。`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|