UNPKG

arale-qrcode

Version:
287 lines (266 loc) 10.1 kB
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(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(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(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(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 (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 (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(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 (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; width:${width}px; height:${height}px;'>`); s.push(`<img src='${options.image}' width='${options.imageSize}' height='${options.imageSize}' 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 (qrCodeAlg){ let options = this.options; let count = qrCodeAlg.getModuleCount(); let scale = count / options.size; // create svg let 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 (let row = 0; row < count; row++) { for (let col = 0; col < count; col++) { let rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); let 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){ let 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;