Files
dianming/qrcode.js
2026-04-25 10:09:16 +08:00

183 lines
5.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 简单的二维码生成器
* 使用Canvas API实现不依赖外部库
*/
class SimpleQRCode {
constructor() {
// QR码版本和大小映射
this.sizeMap = {
1: 21, 2: 25, 3: 29, 4: 33, 5: 37, 6: 41, 7: 45, 8: 49, 9: 53, 10: 57
};
}
/**
* 生成二维码到Canvas
*/
toCanvas(canvas, text, options = {}) {
const size = options.width || 200;
const margin = options.margin || 2;
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
// 白色背景
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, size, size);
// 生成QR码数据
const qrData = this.generateQRData(text);
// 计算单元格大小
const moduleCount = qrData.length;
const cellSize = (size - margin * 2) / moduleCount;
// 绘制QR码
ctx.fillStyle = '#000000';
for (let row = 0; row < moduleCount; row++) {
for (let col = 0; col < moduleCount; col++) {
if (qrData[row][col]) {
const x = margin + col * cellSize;
const y = margin + row * cellSize;
ctx.fillRect(x, y, cellSize, cellSize);
}
}
}
return canvas;
}
/**
* 生成QR码数据矩阵
*/
generateQRData(text) {
// 简化版本生成一个基本的QR码结构
const version = this.getVersion(text.length);
const size = this.sizeMap[version] || 21;
// 创建数据矩阵
const matrix = Array(size).fill(null).map(() => Array(size).fill(false));
// 添加定位图案
this.addFinderPattern(matrix, 0, 0);
this.addFinderPattern(matrix, size - 7, 0);
this.addFinderPattern(matrix, 0, size - 7);
// 添加定时图案
this.addTimingPattern(matrix, size);
// 添加数据(简化版本)
this.addData(matrix, text, size);
return matrix;
}
/**
* 添加定位图案
*/
addFinderPattern(matrix, row, col) {
// 外框
for (let i = 0; i < 7; i++) {
matrix[row][col + i] = true;
matrix[row + 6][col + i] = true;
matrix[row + i][col] = true;
matrix[row + i][col + 6] = true;
}
// 内框
for (let i = 2; i < 5; i++) {
for (let j = 2; j < 5; j++) {
matrix[row + i][col + j] = true;
}
}
}
/**
* 添加定时图案
*/
addTimingPattern(matrix, size) {
for (let i = 8; i < size - 8; i++) {
matrix[6][i] = i % 2 === 0;
matrix[i][6] = i % 2 === 0;
}
}
/**
* 添加数据(简化版本)
*/
addData(matrix, text, size) {
// 将文本转换为二进制数据
const binaryData = this.textToBinary(text);
let dataIndex = 0;
let upward = true;
// 从右下角开始填充数据
for (let col = size - 1; col > 0; col -= 2) {
if (col === 6) col--; // 跳过定时图案列
for (let row = upward ? size - 1 : 0;
upward ? row >= 0 : row < size;
upward ? row-- : row++) {
for (let c = 0; c < 2; c++) {
const currentCol = col - c;
if (this.isDataArea(row, currentCol, size)) {
if (dataIndex < binaryData.length) {
matrix[row][currentCol] = binaryData[dataIndex] === '1';
dataIndex++;
}
}
}
}
upward = !upward;
}
}
/**
* 检查是否为数据区域
*/
isDataArea(row, col, size) {
// 检查是否在定位图案区域
if (row < 9 && col < 9) return false;
if (row < 9 && col > size - 9) return false;
if (row > size - 9 && col < 9) return false;
// 检查是否在定时图案区域
if (row === 6 || col === 6) return false;
return true;
}
/**
* 文本转二进制
*/
textToBinary(text) {
let binary = '';
for (let i = 0; i < text.length; i++) {
const charCode = text.charCodeAt(i);
binary += charCode.toString(2).padStart(8, '0');
}
return binary;
}
/**
* 根据数据长度获取QR码版本
*/
getVersion(length) {
if (length <= 17) return 1;
if (length <= 32) return 2;
if (length <= 53) return 3;
if (length <= 78) return 4;
if (length <= 106) return 5;
if (length <= 134) return 6;
if (length <= 154) return 7;
if (length <= 192) return 8;
if (length <= 230) return 9;
return 10;
}
}
// 创建全局实例
window.SimpleQRCode = SimpleQRCode;