feat: 二维码生成插件

master
LCJ-MinYa 1 week ago
parent 075ca62f7e
commit e7240d694a

@ -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} 块内容生成二维码。`);
} }
}
);
});
}); });
}); });

File diff suppressed because one or more lines are too long

@ -13,7 +13,7 @@ body {
.container { .container {
width: 100%; width: 100%;
max-width: 800px; max-width: 1200px;
background-color: #ffffff; background-color: #ffffff;
padding: 24px 48px; padding: 24px 48px;
border-radius: 8px; border-radius: 8px;
@ -41,7 +41,9 @@ textarea#text-input {
font-size: 14px; font-size: 14px;
box-sizing: border-box; box-sizing: border-box;
resize: vertical; /* 允许用户垂直调整大小 */ resize: vertical; /* 允许用户垂直调整大小 */
transition: border-color 0.2s, box-shadow 0.2s; transition:
border-color 0.2s,
box-shadow 0.2s;
} }
textarea#text-input:focus { textarea#text-input:focus {
@ -79,14 +81,16 @@ button#generate-btn:active {
margin-top: 24px; margin-top: 24px;
display: flex; display: flex;
flex-direction: column; /* 垂直排列 */ flex-direction: column; /* 垂直排列 */
align-items: center; /* 居中对齐 */ align-items: stretch; /* 让子项目qrcode-item在交叉轴上填满容器 */
gap: 30px; /* 二维码之间的间距 */ gap: 30px; /* 二维码之间的间距 */
} }
.qrcode-item { .qrcode-item {
width: 100%; /* 明确宽度为100% */
box-sizing: border-box;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center; /* 容器内的内容标签和canvas居中 */
padding: 15px; padding: 15px;
background-color: #fafbfc; background-color: #fafbfc;
border: 1px solid #dfe1e6; border: 1px solid #dfe1e6;
@ -101,13 +105,14 @@ button#generate-btn:active {
} }
/* /*
CSS canvas qrcode-item
auto
*/ */
.qrcode-item img { .qrcode-item canvas {
max-width: 400px; width: 100%;
height: auto; height: auto;
display: block; /* 移除 canvas 作为内联元素可能产生的额外空间 */
background-color: white; background-color: white;
padding: 10px; padding: 10px;
border: 1px solid #ccc; border: 1px solid #ccc;
box-sizing: border-box;
} }

Loading…
Cancel
Save