UNPKG

qrcode-generator-es

Version:
224 lines (223 loc) 7.25 kB
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 = {}; } }