一、二维码基础概念
1.1 二维码的定义
二维码是在一维条码基础上发展的矩阵式二维条码,通过矩形区域内黑白像素矩阵编码信息。与一维条码相比,它无需线性扫描,手机等设备从任意角度拍摄即可快速解码,能存储更丰富的信息,包括文本、链接、联系人信息等。
1.2 主流二维码格式
| 格式 | 特点 | 适用场景 |
|---|---|---|
| QR Code | 正方形,有3个定位角标,支持多种数据,容错率高(15%-30%) | 支付、扫码登录、信息查询等 |
| Data Matrix | 尺寸小、密度高 | 电子元件、药品包装等小型产品 |
| PDF417 | 长方形,存储容量大 | 身份证背面、物流单等 |
| Aztec Code | 无定位角标,识别速度快 | 航空登机牌、门票等 |
二、二维码存储容量计算
2.1 影响存储容量的核心因素
二维码存储容量由版本(Version)、纠错级别(Error Correction Level)和编码模式(Encoding Mode)共同决定。
- 版本:从1到40,决定矩阵大小,公式为模块数=21+4×(版本-1),如版本1为21×21模块,版本40为177×177模块。
- 纠错级别:分为L(7%)、M(15%)、Q(25%)、H(30%),级别越高,容量越小但抗损坏能力越强。
- 编码模式:不同数据类型采用不同编码模式,效率不同。
2.2 不同编码模式的容量差异
| 编码模式 | 处理内容 | 最大容量(版本40-L级) |
|---|---|---|
| 数字(Numeric) | 0-9 | 7,089个数字 |
| 字母数字(Alphanumeric) | A-Z、0-9、部分符号 | 4,296个字符 |
| 字节(Byte) | ASCII、UTF-8 | 2,953字节 |
| 汉字(Kanji) | 中文、日文等双字节字符 | 1,817个字符 |
2.3 容量计算示例
以版本3、纠错级别M的QR码在字母数字模式下的最大容量计算为例:
- 查标准表得版本3-M级的数据码字为44字节。
- 字母数字模式下,2字符对应11bit(1.375字节/字符)。
- 计算:44÷1.375=32字符。
- 验证:44×8=352bit,减去模式标识(4bit)、字符计数(9bit)、终止符(4bit),剩余335bit,335÷11×2≈60.9,取整为32个字符。
三、二维码生成的核心步骤
3.1 数据编码
根据输入数据类型选择编码模式,将数据转化为二进制流,并添加模式标识和字符计数。
// 简单模拟数字模式编码过程(实际由库实现)
function encodeNumeric(data) {
let binary = '';
// 每3个数字转为10位二进制
for (let i = 0; i < data.length; i += 3) {
const chunk = data.substr(i, 3);
const num = parseInt(chunk, 10);
// 转为10位二进制,不足补前导0
binary += num.toString(2).padStart(10, '0');
}
// 添加模式标识(数字模式为0001)和字符计数
return '0001' + data.length.toString(2).padStart(10, '0') + binary;
}
// 示例:编码"123"
console.log(encodeNumeric("123")); // 输出包含模式标识、计数和编码后的二进制
3.2 纠错码生成(基于里德-所罗门算法)
将编码后的二进制流拆分为数据块,通过里德-所罗门算法生成纠错码块,再混合形成完整数据序列。
3.3 矩阵布局与填充
初始化矩阵,绘制定位图案、对齐图案等固定图案,将完整数据序列按“之”字形规则填充,应用掩码优化识别率。
四、qrcode.js前端实现详解
4.1 qrcode.js简介
qrcode.js是轻量、无依赖的前端二维码生成库,支持Canvas和Image渲染,API简洁,适合生成网址、文本等二维码。
4.2 基础使用
4.2.1 引入库
<!-- CDN引入 -->
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js"></script>
4.2.2 生成基本二维码
<!-- 容器 -->
<canvas id="qrcodeCanvas"></canvas>
<img id="qrcodeImg" />
<script>
const content = "https://example.com"; // 二维码内容
// 生成到Canvas
QRCode.toCanvas(document.getElementById("qrcodeCanvas"), content, (error) => {
if (error) console.error(error);
else console.log("Canvas二维码生成成功");
});
// 生成到Image(返回DataURL)
QRCode.toDataURL(content, (error, url) => {
if (error) console.error(error);
else document.getElementById("qrcodeImg").src = url;
});
</script>
4.3 高级配置
<canvas id="customQrcode"></canvas>
<script>
const options = {
width: 300, // 宽度
height: 300, // 高度
color: {
dark: "#3366ff", // 深色模块颜色
light: "#f0f8ff" // 浅色背景颜色
},
errorCorrectionLevel: "H", // 高容错级别
margin: 2 // 边缘留白
};
QRCode.toCanvas(
document.getElementById("customQrcode"),
"自定义样式的二维码",
options,
(error) => {
if (error) console.error(error);
else console.log("自定义二维码生成成功");
}
);
</script>
4.4 动态生成二维码
<input type="text" id="inputContent" placeholder="输入内容生成二维码">
<canvas id="dynamicCanvas"></canvas>
<script>
const input = document.getElementById("inputContent");
const canvas = document.getElementById("dynamicCanvas");
// 输入框变化时重新生成
input.addEventListener("input", (e) => {
const content = e.target.value.trim() || "请输入内容";
QRCode.toCanvas(canvas, content, (error) => {
if (error) console.error(error);
});
});
</script>
4.5 为二维码添加Logo
<canvas id="logoQrcode"></canvas>
<script>
const canvas = document.getElementById("logoQrcode");
const content = "带Logo的二维码";
const logoUrl = "logo.png"; // Logo图片地址
// 生成二维码后添加Logo
QRCode.toCanvas(canvas, content, { errorCorrectionLevel: "H" }, (error) => {
if (error) return console.error(error);
const ctx = canvas.getContext("2d");
const logo = new Image();
logo.crossOrigin = "anonymous"; // 解决跨域
logo.onload = () => {
const logoSize = canvas.width / 5; // Logo大小为二维码的1/5
const x = (canvas.width - logoSize) / 2; // 水平居中
const y = (canvas.height - logoSize) / 2; // 垂直居中
// 绘制Logo背景(白色圆形边框)
ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.arc(x + logoSize/2, y + logoSize/2, logoSize/2 + 5, 0, 2*Math.PI);
ctx.fill();
// 绘制Logo
ctx.drawImage(logo, x, y, logoSize, logoSize);
};
logo.src = logoUrl;
});
</script>
4.6 qrcode.js中数据填充和补位过程
4.6.1 补位过程
补位用于解决数据长度不足的问题,使用固定序列11101100(0xEC)和00010001(0x11)循环填充。
// 模拟补位过程(简化版)
function padData(dataBits, totalBits) {
const padSequence = [0xEC, 0x11]; // 补位序列
let padBits = '';
let remaining = totalBits - dataBits;
let index = 0;
while (remaining > 0) {
// 取补位序列的当前字节转为8位二进制
const byteBits = padSequence[index].toString(2).padStart(8, '0');
// 取需要的位数
const take = Math.min(remaining, 8);
padBits += byteBits.substr(0, take);
remaining -= take;
index = (index + 1) % 2; // 循环切换序列
}
return dataBits + padBits;
}
// 示例:假设数据位420,需要补到448位
const dataBits = '1010...'; // 省略部分二进制
const paddedBits = padData(dataBits, 448);
4.6.2 数据填充过程
将补位后的二进制流转换为数据码字(8位字节),并分割为数据块。
// 模拟数据填充为数据码字并分块(简化版)
function packData(paddedBits, blockCount) {
// 转换为数据码字(每8位一个字节)
const codewords = [];
for (let i = 0; i < paddedBits.length; i += 8) {
const byteStr = paddedBits.substr(i, 8);
codewords.push(parseInt(byteStr, 2));
}
// 分割为数据块
const blockSize = Math.floor(codewords.length / blockCount);
const blocks = [];
let offset = 0;
for (let i = 0; i < blockCount; i++) {
// 最后一个块可能多1个字节
const size = i === blockCount - 1 ? codewords.length - offset : blockSize;
blocks.push(codewords.slice(offset, offset + size));
offset += size;
}
return blocks;
}
// 示例:将补位后的二进制转为数据块(假设分为2块)
const blocks = packData(paddedBits, 2);
五、总结
本指南详细介绍了二维码的基础概念、存储容量计算、生成核心步骤以及qrcode.js的前端实现,包括基础使用、高级配置、动态生成、添加Logo等操作,并解析了qrcode.js中数据填充和补位的过程。通过学习这些知识,初学者可以全面了解二维码技术,并能在前端项目中灵活应用qrcode.js生成符合需求的二维码。



李枭龙10 个月前
AI生成文章:请以上所有知识进行深入分析,确定主要知识点,为每个知识点撰写详细说明并附上具有代表性且带有清晰注释的代码示例,接着根据内容拟定一个准确反映文档核心的标题,最后严格按照 Markdown 格式进行排版,确保文档规范美观,以满足初学者学习使用的需求。
李枭龙1 年前
X Lucas