qrcode-generator-es
Version:
224 lines (223 loc) • 7.25 kB
JavaScript
import qrcodegen from "./qrcodegen.js";
function getIconDefault(option) {
return {
size: 40,
image: null,
...option,
};
}
function tableCellStyleString(size, background) {
const w = Math.floor(size * 100) / 100;
const styleObj = {
width: `${w}px`,
height: `${w}px`,
background,
"border-width": "0px",
"border-style": "none",
"border-collapse": "collapse",
padding: "0px",
margin: "0px",
};
let str = "";
for (const key in styleObj) {
str += `${key}:${styleObj[key]};`;
}
return str;
}
function createQrCode(text, level) {
let ecc = qrcodegen.QrCode.Ecc.LOW;
if (level === "M") {
ecc = qrcodegen.QrCode.Ecc.MEDIUM;
}
else if (level === "Q") {
ecc = qrcodegen.QrCode.Ecc.QUARTILE;
}
else if (level === "H") {
ecc = qrcodegen.QrCode.Ecc.HIGH;
}
return qrcodegen.QrCode.encodeText(text, ecc);
}
export function createElement(el, tagName = "table", namespace = null) {
if (el == null) {
if (namespace == null) {
el = document.createElement(tagName);
}
else {
el = document.createElementNS(namespace, tagName);
}
}
else if (typeof el === "string") {
el = document.querySelector(el);
}
if (el == null) {
throw new Error("el error");
}
if (el.tagName.toUpperCase() !== tagName.toUpperCase()) {
throw new Error("output and tag are mismatch");
}
return el;
}
function loadImage(option, complete) {
if (option.icon != null) {
const img = new Image();
img.src = option.icon.src;
img.onload = () => {
option.icon.image = img;
complete(option);
};
img.onerror = () => {
complete(option);
};
}
else {
complete(option);
}
}
/** 渲染二维码到表格 */
export function renderToTable(qrcode, option) {
const $el = createElement(option.el, "table");
option.el = $el;
const scale = option.size / qrcode.size;
$el.style.cssText =
"border-width:0px;border-style:none;border-collapse:collapse;padding:0px;";
let qrHtml = "<tbody>";
for (let r = 0; r < qrcode.size; r++) {
qrHtml += "<tr>";
for (let c = 0; c < qrcode.size; c++) {
const fill = qrcode.getModule(c, r) ? option.fill : option.background;
const styleStr = tableCellStyleString(scale, fill);
qrHtml += `<td style="${styleStr}"/>`;
}
qrHtml += "</tr>";
}
qrHtml += "</tbody>";
$el.innerHTML = qrHtml;
return $el;
}
export function renderToSvg(qrcode, option) {
const $el = createElement(option.el, "svg", "http://www.w3.org/2000/svg");
option.el = $el;
const size = option.size;
const numCells = qrcode.size;
const parts = [];
for (let y = 0; y < numCells; y++) {
for (let x = 0; x < numCells; x++) {
if (qrcode.getModule(x, y))
parts.push(`M${x},${y}h1v1h-1z`);
}
}
$el.setAttribute("xmlns", "http://www.w3.org/2000/svg");
$el.setAttribute("width", `${size}px`);
$el.setAttribute("height", `${size}px`);
$el.setAttribute("viewBox", `0 0 ${numCells} ${numCells}`);
$el.setAttribute("preserveAspectRatio", "xMinYMin meet");
$el.setAttribute("role", "img");
const partsHtml = [
`<rect width="100%" height="100%" fill="${option.background}"/>`,
`<path d="${parts.join(" ")}" fill="${option.fill}"/>`,
];
if (option.icon != null) {
const iconOpt = getIconDefault(option.icon);
const scale = numCells / size;
const iconSize = Math.floor(Math.min(iconOpt.size, size * 0.3) * scale);
const point = numCells / 2 - iconSize / 2;
partsHtml.push(`<image href="${iconOpt.src}" width="${iconSize}" height="${iconSize}" x="${point}" y="${point}" preserveAspectRatio="none"></image>`);
}
$el.innerHTML = partsHtml.join("");
return $el;
}
export function renderToCanvas(qr, option) {
const canvas = createElement(option.el, "canvas");
loadImage(option, (opts) => {
const scale = opts.size / qr.size;
canvas.width = opts.size;
canvas.height = opts.size;
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let y = 0; y < qr.size; y++) {
for (let x = 0; x < qr.size; x++) {
ctx.fillStyle = qr.getModule(x, y) ? opts.fill : opts.background;
const startX = Math.floor(x * scale);
const ceilScale = Math.ceil(scale);
const startY = Math.floor(y * scale);
ctx.fillRect(startX, startY, ceilScale, ceilScale);
}
}
if (opts.icon != null && opts.icon.image != null) {
const iconOpt = getIconDefault(opts.icon);
const iconSize = Math.floor(Math.min(iconOpt.size, opts.size * 0.3));
const point = opts.size / 2 - iconSize / 2;
ctx.drawImage(opts.icon.image, point, point, iconSize, iconSize);
}
});
return canvas;
}
export function renderToImg(qrcode, option) {
const $el = createElement(option.el, "img");
const opts = { ...option, el: null };
const $canvas = renderToCanvas(qrcode, opts);
$el.style.width = `${$canvas.width}px`;
$el.style.height = `${$canvas.height}px`;
$el.src = $canvas.toDataURL("image/png");
option.el = $el;
return $el;
}
/** 二维码渲染 */
export class QRCodeRender {
constructor(option) {
Object.defineProperty(this, "option", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_defaultOption", {
enumerable: true,
configurable: true,
writable: true,
value: {
size: 100,
level: "M",
el: null,
text: null,
fill: "#000000",
background: "#ffffff",
icon: undefined,
}
});
this.option = { ...this._defaultOption, ...option };
}
/** 渲染二维码 */
render() {
const qrcode = createQrCode(this.option.text || "", this.option.level);
return this.option.renderFn(qrcode, this.option);
}
/** 批量修改渲染配置 */
setOption(option) {
for (const key in option) {
this.set(key, option[key]);
}
}
/** 修改渲染配置项 */
set(key, value) {
const _v = value ? value : this._defaultOption[key];
this.option[key] = _v;
}
/** 添加数据并渲染二维码 */
addData(data) {
let text = this.option.text || "";
text = `${text}${data}`;
return this.resetData(text);
}
/** 重置二维码 */
resetData(data) {
this.option.text = data;
return this.render();
}
destroy() {
this.option.el = null;
this.option.icon = undefined;
this.option.renderFn = undefined;
this.option = {};
}
}