feat: 二维码生成插件

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

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

File diff suppressed because one or more lines are too long

@ -2,112 +2,117 @@
/* 全局样式和基础布局 */
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
background-color: #f4f5f7;
color: #172b4d;
margin: 0;
padding: 20px;
display: flex;
justify-content: center;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
background-color: #f4f5f7;
color: #172b4d;
margin: 0;
padding: 20px;
display: flex;
justify-content: center;
}
.container {
width: 100%;
max-width: 800px;
background-color: #ffffff;
padding: 24px 48px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
box-sizing: border-box;
width: 100%;
max-width: 1200px;
background-color: #ffffff;
padding: 24px 48px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
box-sizing: border-box;
}
h1 {
color: #091e42;
text-align: center;
color: #091e42;
text-align: center;
}
p {
text-align: center;
color: #5e6c84;
margin-bottom: 24px;
text-align: center;
color: #5e6c84;
margin-bottom: 24px;
}
/* 文本输入框样式 */
textarea#text-input {
width: 100%;
padding: 12px;
border: 2px solid #dfe1e6;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
resize: vertical; /* 允许用户垂直调整大小 */
transition: border-color 0.2s, box-shadow 0.2s;
width: 100%;
padding: 12px;
border: 2px solid #dfe1e6;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
resize: vertical; /* 允许用户垂直调整大小 */
transition:
border-color 0.2s,
box-shadow 0.2s;
}
textarea#text-input:focus {
outline: none;
border-color: #4c9aff;
box-shadow: 0 0 0 2px rgba(76, 154, 255, 0.3);
outline: none;
border-color: #4c9aff;
box-shadow: 0 0 0 2px rgba(76, 154, 255, 0.3);
}
/* 按钮样式 */
button#generate-btn {
display: block;
width: 100%;
padding: 12px;
margin: 20px 0;
background-color: #0052cc;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: background-color 0.2s;
display: block;
width: 100%;
padding: 12px;
margin: 20px 0;
background-color: #0052cc;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: background-color 0.2s;
}
button#generate-btn:hover {
background-color: #0065ff;
background-color: #0065ff;
}
button#generate-btn:active {
background-color: #0747a6;
background-color: #0747a6;
}
/* 二维码容器和项目样式 */
#qrcode-container {
margin-top: 24px;
display: flex;
flex-direction: column; /* 垂直排列 */
align-items: center; /* 居中对齐 */
gap: 30px; /* 二维码之间的间距 */
margin-top: 24px;
display: flex;
flex-direction: column; /* 垂直排列 */
align-items: stretch; /* 让子项目qrcode-item在交叉轴上填满容器 */
gap: 30px; /* 二维码之间的间距 */
}
.qrcode-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 15px;
background-color: #fafbfc;
border: 1px solid #dfe1e6;
border-radius: 4px;
width: 100%; /* 明确宽度为100% */
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center; /* 容器内的内容标签和canvas居中 */
padding: 15px;
background-color: #fafbfc;
border: 1px solid #dfe1e6;
border-radius: 4px;
}
.qrcode-label {
font-size: 14px;
font-weight: bold;
color: #5e6c84;
margin-bottom: 10px;
font-size: 14px;
font-weight: bold;
color: #5e6c84;
margin-bottom: 10px;
}
/*
CSS
auto
canvas qrcode-item
*/
.qrcode-item img {
max-width: 400px;
height: auto;
background-color: white;
padding: 10px;
border: 1px solid #ccc;
.qrcode-item canvas {
width: 100%;
height: auto;
display: block; /* 移除 canvas 作为内联元素可能产生的额外空间 */
background-color: white;
padding: 10px;
border: 1px solid #ccc;
box-sizing: border-box;
}

Loading…
Cancel
Save