Skip to content

Instantly share code, notes, and snippets.

@watert
Last active October 24, 2022 02:18
Show Gist options
  • Save watert/c7fe42a54a7130a15a2a2f3e741a6b0f to your computer and use it in GitHub Desktop.
Save watert/c7fe42a54a7130a15a2a2f3e741a6b0f to your computer and use it in GitHub Desktop.
适用于小程序的通用化的 QRCode 生成器
/**
create-qrcode.js
================
基于 qr.js 进行包装, 适用于小程序的通用化的 QRCode JS 生成器
qr.js 是已经比较成熟的二维码生成相关的 JS 移植,然而其内部的渲染机制需要浏览器相关特性的支持,使得在小程序端无法直接使用。
小程序中也无法使用 SVG 直接渲染,而如果使用 DOM 结构渲染或者 Canvas 渲染的话,需要的开发和接入成本也会上升。
因此这里使用的方案是: SVG 字符串生成后,转化为 dataURL 字符串,可以直接作为小程序中的 image 标签的 src 属性直接使用。
因为是 SVG 矢量图, 因此可以渲染为任意尺寸。
### INSTALLATION 安装:
在项目中通过 npm 等包管理器安装 qr.js 即可
```bash
npm i qr.js -S
```
### USAGE 调用方法:
```jsx
import createQRCode from './create-qrcode';
<image src={createQRCode({ value: '二维码内容', fgColor: 'black' })} />
```
参数:
* @param {obj} options
* @param {string} options.level - 容错级别 L|M|Q|H, 默认为 L
* @param {string} options.bgColor - 背景色, 默认为 null, 即不使用背景色
* @param {string} options.fgColor - 前景色, 即二维码的颜色, 默认为黑色
* @param {boolean} opitons.dataURL - 是否使用 dataURL 生成, 默认为 true, 否则会返回原始 SVG 字符串
* @returns
* */
import QRCodeImpl from 'qr.js/lib/QRCode';
import ErrorCorrectLevel from 'qr.js/lib/ErrorCorrectLevel';
function convertStr(str) {
let out = '';
for (let i = 0; i < str.length; i++) {
let charcode = str.charCodeAt(i);
if (charcode < 0x0080) {
out += String.fromCharCode(charcode);
} else if (charcode < 0x0800) {
out += String.fromCharCode(0xc0 | (charcode >> 6));
out += String.fromCharCode(0x80 | (charcode & 0x3f));
} else if (charcode < 0xd800 || charcode >= 0xe000) {
out += String.fromCharCode(0xe0 | (charcode >> 12));
out += String.fromCharCode(0x80 | ((charcode >> 6) & 0x3f));
out += String.fromCharCode(0x80 | (charcode & 0x3f));
} else {
// This is a surrogate pair, so we'll reconsitute the pieces and work
// from that
i += 1;
charcode = 0x10000 + (((charcode & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
out += String.fromCharCode(0xf0 | (charcode >> 18));
out += String.fromCharCode(0x80 | ((charcode >> 12) & 0x3f));
out += String.fromCharCode(0x80 | ((charcode >> 6) & 0x3f));
out += String.fromCharCode(0x80 | (charcode & 0x3f));
}
}
return out;
}
function generatePath(modules, margin) {
const ops = [];
modules.forEach((row, y) => {
let start = null;
row.forEach((cell, x) => {
if (!cell && start !== null) {
// M0 0h7v1H0z injects the space with the move and drops the comma,
// saving a char per operation
ops.push(
`M${start + margin} ${y + margin}h${x - start}v1H${start + margin}z`,
);
start = null;
return;
}
// end of row, clean up or skip
if (x === row.length - 1) {
if (!cell) {
// We would have closed the op above already so this can only mean
// 2+ light modules in a row.
return;
}
if (start === null) {
// Just a single dark module.
ops.push(`M${x + margin},${y + margin} h1v1H${x + margin}z`);
} else {
// Otherwise finish the current line.
ops.push(
`M${start + margin},${y + margin} h${(x + 1) - start}v1H${start
+ margin}z`,
);
}
return;
}
if (cell && start === null) {
start = x;
}
});
});
return ops.join('');
}
/**
*
* @param {obj} options
* @param {string} options.level - 容错级别 L|M|Q|H, 默认为 L
* @param {string} options.bgColor - 背景色, 默认为 null, 即不使用背景色
* @param {string} options.fgColor - 前景色, 即二维码的颜色, 默认为黑色
* @param {boolean} opitons.dataURL - 是否使用 dataURL 生成, 默认为 true, 否则会返回原始 SVG 字符串
* @returns
*/
export default function createSvg(options) {
if (typeof options === 'string') options = { value: options };
options = {
level: 'L', // L | M | Q | H
bgColor: null,
fgColor: '#000000',
includeMargin: false,
margin: 0,
dataURL: true,
...options,
};
const { bgColor, fgColor, value, level, dataURL, margin } = options;
const qrcode = new QRCodeImpl(-1, ErrorCorrectLevel[level]);
qrcode.addData(convertStr(value));
qrcode.make();
const cells = qrcode.modules;
const numCells = cells.length + (margin * 2);
const path = generatePath(cells, margin);
const viewBox = `0 0 ${numCells} ${numCells}`;
const bgPath = bgColor ? `<path fill="${bgColor}" d="M0,0 h${numCells} v${numCells} H0z" />` : '';
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}"
preserveAspectRatio="xMidYMid meet" style="vertical-align: middle;"
>
<g>
${bgPath}
<path fill="${fgColor}" d="${path}"></path>
</g>
</svg>`;
if (dataURL) return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
return svg;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment