183 lines
5.0 KiB
JavaScript
183 lines
5.0 KiB
JavaScript
/**
|
||
* 简单的二维码生成器
|
||
* 使用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;
|