UNPKG

arale-qrcode

Version:
270 lines (249 loc) 10.3 kB
'use strict'; var extend = require('spm-extend'); var qrcodeAlgObjCache = []; var QRCodeAlg = require('./qrcodealg'); /** * 计算矩阵点的前景色 * @param {Obj} config * @param {Number} config.row 点x坐标 * @param {Number} config.col 点y坐标 * @param {Number} config.count 矩阵大小 * @param {Number} config.options 组件的options * @return {String} */ var getForeGround = function getForeGround(config) { var options = config.options; if (options.pdground && (config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5 || config.row > config.count - 6 && config.row < config.count - 2 && config.col > 1 && config.col < 5 || config.row > 1 && config.row < 5 && config.col > config.count - 6 && config.col < config.count - 2)) { return options.pdground; } return options.foreground; }; /** * 点是否在Position Detection * @param {row} 矩阵行 * @param {col} 矩阵列 * @param {count} 矩阵大小 * @return {Boolean} */ var inPositionDetection = function inPositionDetection(row, col, count) { if (row < 7 && col < 7 || row > count - 8 && col < 7 || row < 7 && col > count - 8) { return true; } return false; }; /** * 获取当前屏幕的设备像素比 devicePixelRatio/backingStore * @param {context} 当前 canvas 上下文,可以为 window */ var getPixelRatio = function getPixelRatio(context) { var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1; return (window.devicePixelRatio || 1) / backingStore; }; /** * 二维码构造函数,主要用于绘制 * @param {参数列表} opt 传递参数 * @return {} */ var qrcode = function qrcode(opt) { if (typeof opt === 'string') { // 只编码ASCII字符串 opt = { text: opt }; } //设置默认参数 this.options = extend({}, { text: '', render: '', size: 256, correctLevel: 3, background: '#ffffff', foreground: '#000000', image: '', imageSize: 30 }, opt); //使用QRCodeAlg创建二维码结构 var qrCodeAlg = null; for (var i = 0, l = qrcodeAlgObjCache.length; i < l; i++) { if (qrcodeAlgObjCache[i].text == this.options.text && qrcodeAlgObjCache[i].text.correctLevel == this.options.correctLevel) { qrCodeAlg = qrcodeAlgObjCache[i].obj; break; } } if (i == l) { qrCodeAlg = new QRCodeAlg(this.options.text, this.options.correctLevel); qrcodeAlgObjCache.push({ text: this.options.text, correctLevel: this.options.correctLevel, obj: qrCodeAlg }); } if (this.options.render) { switch (this.options.render) { case 'canvas': return this.createCanvas(qrCodeAlg); case 'table': return this.createTable(qrCodeAlg); case 'svg': return this.createSVG(qrCodeAlg); default: return this.createDefault(qrCodeAlg); } } return this.createDefault(qrCodeAlg); }; extend(qrcode.prototype, { // default create canvas -> svg -> table createDefault: function createDefault(qrCodeAlg) { var canvas = document.createElement('canvas'); if (canvas.getContext) { return this.createCanvas(qrCodeAlg); } var SVG_NS = 'http://www.w3.org/2000/svg'; if (!!document.createElementNS && !!document.createElementNS(SVG_NS, 'svg').createSVGRect) { return this.createSVG(qrCodeAlg); } return this.createTable(qrCodeAlg); }, // canvas create createCanvas: function createCanvas(qrCodeAlg) { var options = this.options; var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); var count = qrCodeAlg.getModuleCount(); var ratio = getPixelRatio(ctx); var size = options.size; var ratioSize = size * ratio; var ratioImgSize = options.imageSize * ratio; // preload img var loadImage = function loadImage(url, callback) { var img = new Image(); img.src = url; img.onload = function () { callback(this); img.onload = null; }; }; //计算每个点的长宽 var tileW = (ratioSize / count).toPrecision(4); var tileH = (ratioSize / count).toPrecision(4); canvas.width = ratioSize; canvas.height = ratioSize; //绘制 for (var row = 0; row < count; row++) { for (var col = 0; col < count; col++) { var w = Math.ceil((col + 1) * tileW) - Math.floor(col * tileW); var h = Math.ceil((row + 1) * tileW) - Math.floor(row * tileW); var foreground = getForeGround({ row: row, col: col, count: count, options: options }); ctx.fillStyle = qrCodeAlg.modules[row][col] ? foreground : options.background; ctx.fillRect(Math.round(col * tileW), Math.round(row * tileH), w, h); } } if (options.image) { loadImage(options.image, function (img) { var x = ((ratioSize - ratioImgSize) / 2).toFixed(2); var y = ((ratioSize - ratioImgSize) / 2).toFixed(2); ctx.drawImage(img, x, y, ratioImgSize, ratioImgSize); }); } canvas.style.width = size + 'px'; canvas.style.height = size + 'px'; return canvas; }, // table create createTable: function createTable(qrCodeAlg) { var options = this.options; var count = qrCodeAlg.getModuleCount(); // 计算每个节点的长宽;取整,防止点之间出现分离 var tileW = Math.floor(options.size / count); var tileH = Math.floor(options.size / count); if (tileW <= 0) { tileW = count < 80 ? 2 : 1; } if (tileH <= 0) { tileH = count < 80 ? 2 : 1; } //创建table节点 //重算码大小 var s = []; s.push('<table style="border:0px; margin:0px; padding:0px; border-collapse:collapse; background-color:' + options.background + ';">'); // 绘制二维码 for (var row = 0; row < count; row++) { s.push('<tr style="border:0px; margin:0px; padding:0px; height:' + tileH + 'px">'); for (var col = 0; col < count; col++) { var foreground = getForeGround({ row: row, col: col, count: count, options: options }); if (qrCodeAlg.modules[row][col]) { s.push('<td style="border:0px; margin:0px; padding:0px; width:' + tileW + 'px; background-color:' + foreground + '"></td>'); } else { s.push('<td style="border:0px; margin:0px; padding:0px; width:' + tileW + 'px; background-color:' + options.background + '"></td>'); } } s.push('</tr>'); } s.push('</table>'); if (options.image) { // 计算表格的总大小 var width = tileW * count; var height = tileH * count; var x = ((width - options.imageSize) / 2).toFixed(2); var y = ((height - options.imageSize) / 2).toFixed(2); s.unshift('<div style=\'position:relative;\n width:' + width + 'px;\n height:' + height + 'px;\'>'); s.push('<img src=\'' + options.image + '\'\n width=\'' + options.imageSize + '\'\n height=\'' + options.imageSize + '\'\n style=\'position:absolute;left:' + x + 'px; top:' + y + 'px;\'>'); s.push('</div>'); } var span = document.createElement('span'); span.innerHTML = s.join(''); return span.firstChild; }, // create svg createSVG: function createSVG(qrCodeAlg) { var options = this.options; var count = qrCodeAlg.getModuleCount(); var scale = count / options.size; // create svg var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('width', options.size); svg.setAttribute('height', options.size); svg.setAttribute('viewBox', '0 0 ' + count + ' ' + count); for (var row = 0; row < count; row++) { for (var col = 0; col < count; col++) { var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); var foreground = getForeGround({ row: row, col: col, count: count, options: options }); rect.setAttribute('x', col); rect.setAttribute('y', row); rect.setAttribute('width', 1); rect.setAttribute('height', 1); rect.setAttribute('stroke-width', 0); if (qrCodeAlg.modules[row][col]) { rect.setAttribute('fill', foreground); } else { rect.setAttribute('fill', options.background); } svg.appendChild(rect); } } // create image if (options.image) { var img = document.createElementNS('http://www.w3.org/2000/svg', 'image'); img.setAttributeNS('http://www.w3.org/1999/xlink', 'href', options.image); img.setAttribute('x', ((count - options.imageSize * scale) / 2).toFixed(2)); img.setAttribute('y', ((count - options.imageSize * scale) / 2).toFixed(2)); img.setAttribute('width', options.imageSize * scale); img.setAttribute('height', options.imageSize * scale); svg.appendChild(img); } return svg; } }); module.exports = qrcode;