This commit is contained in:
Harry
2026-04-25 10:09:16 +08:00
committed by GitHub
commit bf215c4b9f
15 changed files with 2482 additions and 0 deletions

182
qrcode.js Normal file
View File

@@ -0,0 +1,182 @@
/**
* 简单的二维码生成器
* 使用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;