uniapp applet to generate QR code (compatible with H5, WeChat applet)

Background:
When buying a movie ticket,
the normal text of the generated ticket collection code generates a two-dimensional code, and the recognized value is also a normal text, for example: barcode, the contents of the movie ticket collection code are all ordinary text;


Platform differences:
1. jquery.qrcode,js (used by H5)
is a JavaScript library for generating QR codes, mainly by obtaining DOM tags, and then drawing them through HTML5 Canvas, without relying on any library.
Small programs do not have the concept of DOM, so they cannot be used universally;
2. weapp-qrcode-canvas-2d 
https://developers.weixin.qq.com/community/develop/article/doc/000e88e73a415835ed9b46d7956013

Mainly introduce the applet to generate QR code plug-ins:

Introduction :
weapp-qrcode-canvas-2d is a js package that uses the new canvas-2d interface to generate QR codes (external QR codes) in WeChat applets. The canvas 2d interface supports same-layer rendering and has better performance. It is recommended to switch to use, which can greatly improve the speed;
warehouse address:

Use :
1. <canvas type="2d" style="width: 127px; height: 127px;position: fixed;top: -1000px;" id="myQrcode"></canvas>Note: Cannot be inside v-
if , otherwise the node will not be found 
2. import drawQrcode from '@/common/qrcodeJs/qrcode.js';
Note: You can also install npm install weapp-qrcode-canvas-2d --save
3. This is in the project For practical use, without superimposing pictures

/**
* 生成二维码(没有使用叠加图片)
* text - 码值
* */
handleMakeQrcode(text) {
    return new Promise((resolve, reject) => {
    	const query = uni.createSelectorQuery()
    	query.select('#myQrcode').fields({
    		node: true,
    		size: true
    	}).exec((res) => {
    		// 获取node节点实例,目前仅微信,快手,京东小程序支持;
    		// #ifdef MP-WEIXIN || MP-KUAISHOU
    		let canvas = res[0].node
    		// #endif
    		// 调用方法drawQrcode生成二维码
    		drawQrcode({
    			// #ifdef MP-WEIXIN || MP-KUAISHOU
    			canvas: canvas,
    			// #endif
    			canvasId: 'myQrcode',
    			width: 127,
    			padding: 0,
    			background: '#ffffff',
    			foreground: '#000000',
    			text: text,
    		})
    		// 获取临时路径
    		uni.canvasToTempFilePath({
    			canvasId: 'myQrcode',
                // #ifdef MP-WEIXIN || MP-KUAISHOU
    			canvas: canvas,
                // #endif
    			x: 0,
    			y: 0,
    			width: 127,
    			height: 127,
    			destWidth: 127,
    			destHeight: 127,
    			success(res) {
                           resolve(res.tempFilePath); // 临时路径
    			},
    			fail(res) {
    			    console.error(res);
                           reject();
    			}
    		})
    	});
    })
}
/*
* 文档中示例 - 有叠加图片的(在二维码中加logo)
*/
const query = wx.createSelectorQuery()

query.select('#myQrcode')
    .fields({
        node: true,
        size: true
    })
    .exec(async (res) => {
        var canvas = res[0].node

        var img = canvas.createImage();
        img.src = "/image/logo.png"

        img.onload = function () {
            // img.onload完成后才能调用 drawQrcode方法

            var options = {
                canvas: canvas,
                canvasId: 'myQrcode',
                width: 260,
                padding: 30,
                paddingColor: '#fff',
                background: '#fff',
                foreground: '#000000',
                text: 'https://gitee.com/WeiDoctor/weapp-qrcode-canvas-2d',
                image: {
                    imageResource: img,
                    width: 80, // 建议不要设置过大,以免影响扫码
                    height: 80 // 建议不要设置过大,以免影响扫码
                    round: true // Logo图片是否为圆形
                }
            }

            drawQrcode(options)

            // 获取临时路径(得到之后,想干嘛就干嘛了)
            wx.canvasToTempFilePath({
                x: 0,
                y: 0,
                width: 260,
                height: 260,
                destWidth: 600,
                destHeight: 600,
                canvasId: 'myQrcode',
                canvas: canvas,
                success(res) {
                    console.log('二维码临时路径为:', res.tempFilePath)
                },
                fail(res) {
                    console.error(res)
                }
            })

        };
    })

Notes:
1. Because the Api in the above warehouse is the Api on the WeChat side, you will later modify it to a cross-platform Api from uniapp, which has been tested to support WeChat and H5; 2. uni.createSelectorQuery()
NodesRef is used to obtain the node information object
Get Node node instance. Currently supports the acquisition of canvas, and only supports WeChat applets, Kuaishou, and JD applets;
so the canvas instance is not passed where it is not supported,
// When the instance cannot be obtained, create a canvas and return canvasContext
ctx = options._this ? uni .createCanvasContext && uni.createCanvasContext(options.canvasId, options._this) : uni.createCanvasContext && uni.createCanvasContext(options.canvasId);

The qrcode needed to generate the QR code;



/**
 * weapp.qrcode.js v1.1.2 https://developers.weixin.qq.com/community/develop/article/doc/000e88e73a415835ed9b46d7956013
 */

(function(global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
		typeof define === 'function' && define.amd ? define(factory) :
		(global.drawQrcode = factory());
}(this, (function() {
	'use strict';

	var hasOwn = Object.prototype.hasOwnProperty;
	var toStr = Object.prototype.toString;
	var defineProperty = Object.defineProperty;
	var gOPD = Object.getOwnPropertyDescriptor;

	var isArray = function isArray(arr) {
		if (typeof Array.isArray === 'function') {
			return Array.isArray(arr);
		}

		return toStr.call(arr) === '[object Array]';
	};

	var isPlainObject = function isPlainObject(obj) {
		if (!obj || toStr.call(obj) !== '[object Object]') {
			return false;
		}

		var hasOwnConstructor = hasOwn.call(obj, 'constructor');
		var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj
			.constructor.prototype, 'isPrototypeOf');
		// Not own constructor property must be Object
		if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
			return false;
		}

		// Own properties are enumerated firstly, so to speed up,
		// if last one is own, then all properties are own.
		var key;
		for (key in obj) {
			/**/ }

		return typeof key === 'undefined' || hasOwn.call(obj, key);
	};

	// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target
	var setProperty = function setProperty(target, options) {
		if (defineProperty && options.name === '__proto__') {
			defineProperty(target, options.name, {
				enumerable: true,
				configurable: true,
				value: options.newValue,
				writable: true
			});
		} else {
			target[options.name] = options.newValue;
		}
	};

	// Return undefined instead of __proto__ if '__proto__' is not an own property
	var getProperty = function getProperty(obj, name) {
		if (name === '__proto__') {
			if (!hasOwn.call(obj, name)) {
				return void 0;
			} else if (gOPD) {
				// In early versions of node, obj['__proto__'] is buggy when obj has
				// __proto__ as an own property. Object.getOwnPropertyDescriptor() works.
				return gOPD(obj, name).value;
			}
		}

		return obj[name];
	};

	var extend = function extend() {
		var options, name, src, copy, copyIsArray, clone;
		var target = arguments[0];
		var i = 1;
		var length = arguments.length;
		var deep = false;

		// Handle a deep copy situation
		if (typeof target === 'boolean') {
			deep = target;
			target = arguments[1] || {};
			// skip the boolean and the target
			i = 2;
		}
		if (target == null || (typeof target !== 'object' && typeof target !== 'function')) {
			target = {};
		}

		for (; i < length; ++i) {
			options = arguments[i];
			// Only deal with non-null/undefined values
			if (options != null) {
				// Extend the base object
				for (name in options) {
					src = getProperty(target, name);
					copy = getProperty(options, name);

					// Prevent never-ending loop
					if (target !== copy) {
						// Recurse if we're merging plain objects or arrays
						if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
							if (copyIsArray) {
								copyIsArray = false;
								clone = src && isArray(src) ? src : [];
							} else {
								clone = src && isPlainObject(src) ? src : {};
							}

							// Never move original objects, clone them
							setProperty(target, {
								name: name,
								newValue: extend(deep, clone, copy)
							});

							// Don't bring in undefined values
						} else if (typeof copy !== 'undefined') {
							setProperty(target, {
								name: name,
								newValue: copy
							});
						}
					}
				}
			}
		}

		// Return the modified object
		return target;
	};

	//---------------------------------------------------------------------
	// QRCode for JavaScript
	//
	// Copyright (c) 2009 Kazuhiko Arase
	//
	// URL: http://www.d-project.com/
	//
	// Licensed under the MIT license:
	//   http://www.opensource.org/licenses/mit-license.php
	//
	// The word "QR Code" is registered trademark of 
	// DENSO WAVE INCORPORATED
	//   http://www.denso-wave.com/qrcode/faqpatent-e.html
	//
	//---------------------------------------------------------------------

	//---------------------------------------------------------------------
	// QR8bitByte
	//---------------------------------------------------------------------

	function QR8bitByte(data) {
		this.mode = QRMode.MODE_8BIT_BYTE;
		this.data = data;
	}

	QR8bitByte.prototype = {

		getLength: function(buffer) {
			return this.data.length;
		},

		write: function(buffer) {
			for (var i = 0; i < this.data.length; i++) {
				// not JIS ...
				buffer.put(this.data.charCodeAt(i), 8);
			}
		}
	};

	//---------------------------------------------------------------------
	// QRCode
	//---------------------------------------------------------------------

	function QRCode(typeNumber, errorCorrectLevel) {
		this.typeNumber = typeNumber;
		this.errorCorrectLevel = errorCorrectLevel;
		this.modules = null;
		this.moduleCount = 0;
		this.dataCache = null;
		this.dataList = new Array();
	}

	QRCode.prototype = {

		addData: function(data) {
			var newData = new QR8bitByte(data);
			this.dataList.push(newData);
			this.dataCache = null;
		},

		isDark: function(row, col) {
			if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
				throw new Error(row + "," + col);
			}
			return this.modules[row][col];
		},

		getModuleCount: function() {
			return this.moduleCount;
		},

		make: function() {
			// Calculate automatically typeNumber if provided is < 1
			if (this.typeNumber < 1) {
				var typeNumber = 1;
				for (typeNumber = 1; typeNumber < 40; typeNumber++) {
					var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, this.errorCorrectLevel);

					var buffer = new QRBitBuffer();
					var totalDataCount = 0;
					for (var i = 0; i < rsBlocks.length; i++) {
						totalDataCount += rsBlocks[i].dataCount;
					}

					for (var i = 0; i < this.dataList.length; i++) {
						var data = this.dataList[i];
						buffer.put(data.mode, 4);
						buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
						data.write(buffer);
					}
					if (buffer.getLengthInBits() <= totalDataCount * 8) break;
				}
				this.typeNumber = typeNumber;
			}
			this.makeImpl(false, this.getBestMaskPattern());
		},

		makeImpl: function(test, maskPattern) {

			this.moduleCount = this.typeNumber * 4 + 17;
			this.modules = new Array(this.moduleCount);

			for (var row = 0; row < this.moduleCount; row++) {

				this.modules[row] = new Array(this.moduleCount);

				for (var col = 0; col < this.moduleCount; col++) {
					this.modules[row][col] = null; //(col + row) % 3;
				}
			}

			this.setupPositionProbePattern(0, 0);
			this.setupPositionProbePattern(this.moduleCount - 7, 0);
			this.setupPositionProbePattern(0, this.moduleCount - 7);
			this.setupPositionAdjustPattern();
			this.setupTimingPattern();
			this.setupTypeInfo(test, maskPattern);

			if (this.typeNumber >= 7) {
				this.setupTypeNumber(test);
			}

			if (this.dataCache == null) {
				this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this
					.dataList);
			}

			this.mapData(this.dataCache, maskPattern);
		},

		setupPositionProbePattern: function(row, col) {

			for (var r = -1; r <= 7; r++) {

				if (row + r <= -1 || this.moduleCount <= row + r) continue;

				for (var c = -1; c <= 7; c++) {

					if (col + c <= -1 || this.moduleCount <= col + c) continue;

					if (0 <= r && r <= 6 && (c == 0 || c == 6) || 0 <= c && c <= 6 && (r == 0 ||
							r == 6) || 2 <= r && r <= 4 && 2 <= c && c <= 4) {
						this.modules[row + r][col + c] = true;
					} else {
						this.modules[row + r][col + c] = false;
					}
				}
			}
		},

		getBestMaskPattern: function() {

			var minLostPoint = 0;
			var pattern = 0;

			for (var i = 0; i < 8; i++) {

				this.makeImpl(true, i);

				var lostPoint = QRUtil.getLostPoint(this);

				if (i == 0 || minLostPoint > lostPoint) {
					minLostPoint = lostPoint;
					pattern = i;
				}
			}

			return pattern;
		},

		createMovieClip: function(target_mc, instance_name, depth) {

			var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth);
			var cs = 1;

			this.make();

			for (var row = 0; row < this.modules.length; row++) {

				var y = row * cs;

				for (var col = 0; col < this.modules[row].length; col++) {

					var x = col * cs;
					var dark = this.modules[row][col];

					if (dark) {
						qr_mc.beginFill(0, 100);
						qr_mc.moveTo(x, y);
						qr_mc.lineTo(x + cs, y);
						qr_mc.lineTo(x + cs, y + cs);
						qr_mc.lineTo(x, y + cs);
						qr_mc.endFill();
					}
				}
			}

			return qr_mc;
		},

		setupTimingPattern: function() {

			for (var r = 8; r < this.moduleCount - 8; r++) {
				if (this.modules[r][6] != null) {
					continue;
				}
				this.modules[r][6] = r % 2 == 0;
			}

			for (var c = 8; c < this.moduleCount - 8; c++) {
				if (this.modules[6][c] != null) {
					continue;
				}
				this.modules[6][c] = c % 2 == 0;
			}
		},

		setupPositionAdjustPattern: function() {

			var pos = QRUtil.getPatternPosition(this.typeNumber);

			for (var i = 0; i < pos.length; i++) {

				for (var j = 0; j < pos.length; j++) {

					var row = pos[i];
					var col = pos[j];

					if (this.modules[row][col] != null) {
						continue;
					}

					for (var r = -2; r <= 2; r++) {

						for (var c = -2; c <= 2; c++) {

							if (r == -2 || r == 2 || c == -2 || c == 2 || r == 0 && c == 0) {
								this.modules[row + r][col + c] = true;
							} else {
								this.modules[row + r][col + c] = false;
							}
						}
					}
				}
			}
		},

		setupTypeNumber: function(test) {

			var bits = QRUtil.getBCHTypeNumber(this.typeNumber);

			for (var i = 0; i < 18; i++) {
				var mod = !test && (bits >> i & 1) == 1;
				this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
			}

			for (var i = 0; i < 18; i++) {
				var mod = !test && (bits >> i & 1) == 1;
				this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
			}
		},

		setupTypeInfo: function(test, maskPattern) {

			var data = this.errorCorrectLevel << 3 | maskPattern;
			var bits = QRUtil.getBCHTypeInfo(data);

			// vertical   
			for (var i = 0; i < 15; i++) {

				var mod = !test && (bits >> i & 1) == 1;

				if (i < 6) {
					this.modules[i][8] = mod;
				} else if (i < 8) {
					this.modules[i + 1][8] = mod;
				} else {
					this.modules[this.moduleCount - 15 + i][8] = mod;
				}
			}

			// horizontal
			for (var i = 0; i < 15; i++) {

				var mod = !test && (bits >> i & 1) == 1;

				if (i < 8) {
					this.modules[8][this.moduleCount - i - 1] = mod;
				} else if (i < 9) {
					this.modules[8][15 - i - 1 + 1] = mod;
				} else {
					this.modules[8][15 - i - 1] = mod;
				}
			}

			// fixed module
			this.modules[this.moduleCount - 8][8] = !test;
		},

		mapData: function(data, maskPattern) {

			var inc = -1;
			var row = this.moduleCount - 1;
			var bitIndex = 7;
			var byteIndex = 0;

			for (var col = this.moduleCount - 1; col > 0; col -= 2) {

				if (col == 6) col--;

				while (true) {

					for (var c = 0; c < 2; c++) {

						if (this.modules[row][col - c] == null) {

							var dark = false;

							if (byteIndex < data.length) {
								dark = (data[byteIndex] >>> bitIndex & 1) == 1;
							}

							var mask = QRUtil.getMask(maskPattern, row, col - c);

							if (mask) {
								dark = !dark;
							}

							this.modules[row][col - c] = dark;
							bitIndex--;

							if (bitIndex == -1) {
								byteIndex++;
								bitIndex = 7;
							}
						}
					}

					row += inc;

					if (row < 0 || this.moduleCount <= row) {
						row -= inc;
						inc = -inc;
						break;
					}
				}
			}
		}

	};

	QRCode.PAD0 = 0xEC;
	QRCode.PAD1 = 0x11;

	QRCode.createData = function(typeNumber, errorCorrectLevel, dataList) {

		var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);

		var buffer = new QRBitBuffer();

		for (var i = 0; i < dataList.length; i++) {
			var data = dataList[i];
			buffer.put(data.mode, 4);
			buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
			data.write(buffer);
		}

		// calc num max data.
		var totalDataCount = 0;
		for (var i = 0; i < rsBlocks.length; i++) {
			totalDataCount += rsBlocks[i].dataCount;
		}

		if (buffer.getLengthInBits() > totalDataCount * 8) {
			throw new Error("code length overflow. (" + buffer.getLengthInBits() + ">" +
				totalDataCount * 8 + ")");
		}

		// end code
		if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
			buffer.put(0, 4);
		}

		// padding
		while (buffer.getLengthInBits() % 8 != 0) {
			buffer.putBit(false);
		}

		// padding
		while (true) {

			if (buffer.getLengthInBits() >= totalDataCount * 8) {
				break;
			}
			buffer.put(QRCode.PAD0, 8);

			if (buffer.getLengthInBits() >= totalDataCount * 8) {
				break;
			}
			buffer.put(QRCode.PAD1, 8);
		}

		return QRCode.createBytes(buffer, rsBlocks);
	};

	QRCode.createBytes = function(buffer, rsBlocks) {

		var offset = 0;

		var maxDcCount = 0;
		var maxEcCount = 0;

		var dcdata = new Array(rsBlocks.length);
		var ecdata = new Array(rsBlocks.length);

		for (var r = 0; r < rsBlocks.length; r++) {

			var dcCount = rsBlocks[r].dataCount;
			var ecCount = rsBlocks[r].totalCount - dcCount;

			maxDcCount = Math.max(maxDcCount, dcCount);
			maxEcCount = Math.max(maxEcCount, ecCount);

			dcdata[r] = new Array(dcCount);

			for (var i = 0; i < dcdata[r].length; i++) {
				dcdata[r][i] = 0xff & buffer.buffer[i + offset];
			}
			offset += dcCount;

			var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
			var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);

			var modPoly = rawPoly.mod(rsPoly);
			ecdata[r] = new Array(rsPoly.getLength() - 1);
			for (var i = 0; i < ecdata[r].length; i++) {
				var modIndex = i + modPoly.getLength() - ecdata[r].length;
				ecdata[r][i] = modIndex >= 0 ? modPoly.get(modIndex) : 0;
			}
		}

		var totalCodeCount = 0;
		for (var i = 0; i < rsBlocks.length; i++) {
			totalCodeCount += rsBlocks[i].totalCount;
		}

		var data = new Array(totalCodeCount);
		var index = 0;

		for (var i = 0; i < maxDcCount; i++) {
			for (var r = 0; r < rsBlocks.length; r++) {
				if (i < dcdata[r].length) {
					data[index++] = dcdata[r][i];
				}
			}
		}

		for (var i = 0; i < maxEcCount; i++) {
			for (var r = 0; r < rsBlocks.length; r++) {
				if (i < ecdata[r].length) {
					data[index++] = ecdata[r][i];
				}
			}
		}

		return data;
	};

	//---------------------------------------------------------------------
	// QRMode
	//---------------------------------------------------------------------

	var QRMode = {
		MODE_NUMBER: 1 << 0,
		MODE_ALPHA_NUM: 1 << 1,
		MODE_8BIT_BYTE: 1 << 2,
		MODE_KANJI: 1 << 3
	};

	//---------------------------------------------------------------------
	// QRErrorCorrectLevel
	//---------------------------------------------------------------------

	var QRErrorCorrectLevel = {
		L: 1,
		M: 0,
		Q: 3,
		H: 2
	};

	//---------------------------------------------------------------------
	// QRMaskPattern
	//---------------------------------------------------------------------

	var QRMaskPattern = {
		PATTERN000: 0,
		PATTERN001: 1,
		PATTERN010: 2,
		PATTERN011: 3,
		PATTERN100: 4,
		PATTERN101: 5,
		PATTERN110: 6,
		PATTERN111: 7
	};

	//---------------------------------------------------------------------
	// QRUtil
	//---------------------------------------------------------------------

	var QRUtil = {

		PATTERN_POSITION_TABLE: [
			[],
			[6, 18],
			[6, 22],
			[6, 26],
			[6, 30],
			[6, 34],
			[6, 22, 38],
			[6, 24, 42],
			[6, 26, 46],
			[6, 28, 50],
			[6, 30, 54],
			[6, 32, 58],
			[6, 34, 62],
			[6, 26, 46, 66],
			[6, 26, 48, 70],
			[6, 26, 50, 74],
			[6, 30, 54, 78],
			[6, 30, 56, 82],
			[6, 30, 58, 86],
			[6, 34, 62, 90],
			[6, 28, 50, 72, 94],
			[6, 26, 50, 74, 98],
			[6, 30, 54, 78, 102],
			[6, 28, 54, 80, 106],
			[6, 32, 58, 84, 110],
			[6, 30, 58, 86, 114],
			[6, 34, 62, 90, 118],
			[6, 26, 50, 74, 98, 122],
			[6, 30, 54, 78, 102, 126],
			[6, 26, 52, 78, 104, 130],
			[6, 30, 56, 82, 108, 134],
			[6, 34, 60, 86, 112, 138],
			[6, 30, 58, 86, 114, 142],
			[6, 34, 62, 90, 118, 146],
			[6, 30, 54, 78, 102, 126, 150],
			[6, 24, 50, 76, 102, 128, 154],
			[6, 28, 54, 80, 106, 132, 158],
			[6, 32, 58, 84, 110, 136, 162],
			[6, 26, 54, 82, 110, 138, 166],
			[6, 30, 58, 86, 114, 142, 170]
		],

		G15: 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0,
		G18: 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0,
		G15_MASK: 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1,

		getBCHTypeInfo: function(data) {
			var d = data << 10;
			while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
				d ^= QRUtil.G15 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15);
			}
			return (data << 10 | d) ^ QRUtil.G15_MASK;
		},

		getBCHTypeNumber: function(data) {
			var d = data << 12;
			while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
				d ^= QRUtil.G18 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18);
			}
			return data << 12 | d;
		},

		getBCHDigit: function(data) {

			var digit = 0;

			while (data != 0) {
				digit++;
				data >>>= 1;
			}

			return digit;
		},

		getPatternPosition: function(typeNumber) {
			return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
		},

		getMask: function(maskPattern, i, j) {

			switch (maskPattern) {

				case QRMaskPattern.PATTERN000:
					return (i + j) % 2 == 0;
				case QRMaskPattern.PATTERN001:
					return i % 2 == 0;
				case QRMaskPattern.PATTERN010:
					return j % 3 == 0;
				case QRMaskPattern.PATTERN011:
					return (i + j) % 3 == 0;
				case QRMaskPattern.PATTERN100:
					return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
				case QRMaskPattern.PATTERN101:
					return i * j % 2 + i * j % 3 == 0;
				case QRMaskPattern.PATTERN110:
					return (i * j % 2 + i * j % 3) % 2 == 0;
				case QRMaskPattern.PATTERN111:
					return (i * j % 3 + (i + j) % 2) % 2 == 0;

				default:
					throw new Error("bad maskPattern:" + maskPattern);
			}
		},

		getErrorCorrectPolynomial: function(errorCorrectLength) {

			var a = new QRPolynomial([1], 0);

			for (var i = 0; i < errorCorrectLength; i++) {
				a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
			}

			return a;
		},

		getLengthInBits: function(mode, type) {

			if (1 <= type && type < 10) {

				// 1 - 9

				switch (mode) {
					case QRMode.MODE_NUMBER:
						return 10;
					case QRMode.MODE_ALPHA_NUM:
						return 9;
					case QRMode.MODE_8BIT_BYTE:
						return 8;
					case QRMode.MODE_KANJI:
						return 8;
					default:
						throw new Error("mode:" + mode);
				}
			} else if (type < 27) {

				// 10 - 26

				switch (mode) {
					case QRMode.MODE_NUMBER:
						return 12;
					case QRMode.MODE_ALPHA_NUM:
						return 11;
					case QRMode.MODE_8BIT_BYTE:
						return 16;
					case QRMode.MODE_KANJI:
						return 10;
					default:
						throw new Error("mode:" + mode);
				}
			} else if (type < 41) {

				// 27 - 40

				switch (mode) {
					case QRMode.MODE_NUMBER:
						return 14;
					case QRMode.MODE_ALPHA_NUM:
						return 13;
					case QRMode.MODE_8BIT_BYTE:
						return 16;
					case QRMode.MODE_KANJI:
						return 12;
					default:
						throw new Error("mode:" + mode);
				}
			} else {
				throw new Error("type:" + type);
			}
		},

		getLostPoint: function(qrCode) {

			var moduleCount = qrCode.getModuleCount();

			var lostPoint = 0;

			// LEVEL1

			for (var row = 0; row < moduleCount; row++) {

				for (var col = 0; col < moduleCount; col++) {

					var sameCount = 0;
					var dark = qrCode.isDark(row, col);

					for (var r = -1; r <= 1; r++) {

						if (row + r < 0 || moduleCount <= row + r) {
							continue;
						}

						for (var c = -1; c <= 1; c++) {

							if (col + c < 0 || moduleCount <= col + c) {
								continue;
							}

							if (r == 0 && c == 0) {
								continue;
							}

							if (dark == qrCode.isDark(row + r, col + c)) {
								sameCount++;
							}
						}
					}

					if (sameCount > 5) {
						lostPoint += 3 + sameCount - 5;
					}
				}
			}

			// LEVEL2

			for (var row = 0; row < moduleCount - 1; row++) {
				for (var col = 0; col < moduleCount - 1; col++) {
					var count = 0;
					if (qrCode.isDark(row, col)) count++;
					if (qrCode.isDark(row + 1, col)) count++;
					if (qrCode.isDark(row, col + 1)) count++;
					if (qrCode.isDark(row + 1, col + 1)) count++;
					if (count == 0 || count == 4) {
						lostPoint += 3;
					}
				}
			}

			// LEVEL3

			for (var row = 0; row < moduleCount; row++) {
				for (var col = 0; col < moduleCount - 6; col++) {
					if (qrCode.isDark(row, col) && !qrCode.isDark(row, col + 1) && qrCode.isDark(
							row, col + 2) && qrCode.isDark(row, col + 3) && qrCode.isDark(row, col +
							4) && !qrCode.isDark(row, col + 5) && qrCode.isDark(row, col + 6)) {
						lostPoint += 40;
					}
				}
			}

			for (var col = 0; col < moduleCount; col++) {
				for (var row = 0; row < moduleCount - 6; row++) {
					if (qrCode.isDark(row, col) && !qrCode.isDark(row + 1, col) && qrCode.isDark(
							row + 2, col) && qrCode.isDark(row + 3, col) && qrCode.isDark(row + 4,
							col) && !qrCode.isDark(row + 5, col) && qrCode.isDark(row + 6, col)) {
						lostPoint += 40;
					}
				}
			}

			// LEVEL4

			var darkCount = 0;

			for (var col = 0; col < moduleCount; col++) {
				for (var row = 0; row < moduleCount; row++) {
					if (qrCode.isDark(row, col)) {
						darkCount++;
					}
				}
			}

			var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
			lostPoint += ratio * 10;

			return lostPoint;
		}

	};

	//---------------------------------------------------------------------
	// QRMath
	//---------------------------------------------------------------------

	var QRMath = {

		glog: function(n) {

			if (n < 1) {
				throw new Error("glog(" + n + ")");
			}

			return QRMath.LOG_TABLE[n];
		},

		gexp: function(n) {

			while (n < 0) {
				n += 255;
			}

			while (n >= 256) {
				n -= 255;
			}

			return QRMath.EXP_TABLE[n];
		},

		EXP_TABLE: new Array(256),

		LOG_TABLE: new Array(256)

	};

	for (var i = 0; i < 8; i++) {
		QRMath.EXP_TABLE[i] = 1 << i;
	}
	for (var i = 8; i < 256; i++) {
		QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^
			QRMath.EXP_TABLE[i - 8];
	}
	for (var i = 0; i < 255; i++) {
		QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
	}

	//---------------------------------------------------------------------
	// QRPolynomial
	//---------------------------------------------------------------------

	function QRPolynomial(num, shift) {

		if (num.length == undefined) {
			throw new Error(num.length + "/" + shift);
		}

		var offset = 0;

		while (offset < num.length && num[offset] == 0) {
			offset++;
		}

		this.num = new Array(num.length - offset + shift);
		for (var i = 0; i < num.length - offset; i++) {
			this.num[i] = num[i + offset];
		}
	}

	QRPolynomial.prototype = {

		get: function(index) {
			return this.num[index];
		},

		getLength: function() {
			return this.num.length;
		},

		multiply: function(e) {

			var num = new Array(this.getLength() + e.getLength() - 1);

			for (var i = 0; i < this.getLength(); i++) {
				for (var j = 0; j < e.getLength(); j++) {
					num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
				}
			}

			return new QRPolynomial(num, 0);
		},

		mod: function(e) {

			if (this.getLength() - e.getLength() < 0) {
				return this;
			}

			var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));

			var num = new Array(this.getLength());

			for (var i = 0; i < this.getLength(); i++) {
				num[i] = this.get(i);
			}

			for (var i = 0; i < e.getLength(); i++) {
				num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);
			}

			// recursive call
			return new QRPolynomial(num, 0).mod(e);
		}
	};

	//---------------------------------------------------------------------
	// QRRSBlock
	//---------------------------------------------------------------------

	function QRRSBlock(totalCount, dataCount) {
		this.totalCount = totalCount;
		this.dataCount = dataCount;
	}

	QRRSBlock.RS_BLOCK_TABLE = [

		// L
		// M
		// Q
		// H

		// 1
		[1, 26, 19],
		[1, 26, 16],
		[1, 26, 13],
		[1, 26, 9],

		// 2
		[1, 44, 34],
		[1, 44, 28],
		[1, 44, 22],
		[1, 44, 16],

		// 3
		[1, 70, 55],
		[1, 70, 44],
		[2, 35, 17],
		[2, 35, 13],

		// 4    
		[1, 100, 80],
		[2, 50, 32],
		[2, 50, 24],
		[4, 25, 9],

		// 5
		[1, 134, 108],
		[2, 67, 43],
		[2, 33, 15, 2, 34, 16],
		[2, 33, 11, 2, 34, 12],

		// 6
		[2, 86, 68],
		[4, 43, 27],
		[4, 43, 19],
		[4, 43, 15],

		// 7    
		[2, 98, 78],
		[4, 49, 31],
		[2, 32, 14, 4, 33, 15],
		[4, 39, 13, 1, 40, 14],

		// 8
		[2, 121, 97],
		[2, 60, 38, 2, 61, 39],
		[4, 40, 18, 2, 41, 19],
		[4, 40, 14, 2, 41, 15],

		// 9
		[2, 146, 116],
		[3, 58, 36, 2, 59, 37],
		[4, 36, 16, 4, 37, 17],
		[4, 36, 12, 4, 37, 13],

		// 10   
		[2, 86, 68, 2, 87, 69],
		[4, 69, 43, 1, 70, 44],
		[6, 43, 19, 2, 44, 20],
		[6, 43, 15, 2, 44, 16],

		// 11
		[4, 101, 81],
		[1, 80, 50, 4, 81, 51],
		[4, 50, 22, 4, 51, 23],
		[3, 36, 12, 8, 37, 13],

		// 12
		[2, 116, 92, 2, 117, 93],
		[6, 58, 36, 2, 59, 37],
		[4, 46, 20, 6, 47, 21],
		[7, 42, 14, 4, 43, 15],

		// 13
		[4, 133, 107],
		[8, 59, 37, 1, 60, 38],
		[8, 44, 20, 4, 45, 21],
		[12, 33, 11, 4, 34, 12],

		// 14
		[3, 145, 115, 1, 146, 116],
		[4, 64, 40, 5, 65, 41],
		[11, 36, 16, 5, 37, 17],
		[11, 36, 12, 5, 37, 13],

		// 15
		[5, 109, 87, 1, 110, 88],
		[5, 65, 41, 5, 66, 42],
		[5, 54, 24, 7, 55, 25],
		[11, 36, 12],

		// 16
		[5, 122, 98, 1, 123, 99],
		[7, 73, 45, 3, 74, 46],
		[15, 43, 19, 2, 44, 20],
		[3, 45, 15, 13, 46, 16],

		// 17
		[1, 135, 107, 5, 136, 108],
		[10, 74, 46, 1, 75, 47],
		[1, 50, 22, 15, 51, 23],
		[2, 42, 14, 17, 43, 15],

		// 18
		[5, 150, 120, 1, 151, 121],
		[9, 69, 43, 4, 70, 44],
		[17, 50, 22, 1, 51, 23],
		[2, 42, 14, 19, 43, 15],

		// 19
		[3, 141, 113, 4, 142, 114],
		[3, 70, 44, 11, 71, 45],
		[17, 47, 21, 4, 48, 22],
		[9, 39, 13, 16, 40, 14],

		// 20
		[3, 135, 107, 5, 136, 108],
		[3, 67, 41, 13, 68, 42],
		[15, 54, 24, 5, 55, 25],
		[15, 43, 15, 10, 44, 16],

		// 21
		[4, 144, 116, 4, 145, 117],
		[17, 68, 42],
		[17, 50, 22, 6, 51, 23],
		[19, 46, 16, 6, 47, 17],

		// 22
		[2, 139, 111, 7, 140, 112],
		[17, 74, 46],
		[7, 54, 24, 16, 55, 25],
		[34, 37, 13],

		// 23
		[4, 151, 121, 5, 152, 122],
		[4, 75, 47, 14, 76, 48],
		[11, 54, 24, 14, 55, 25],
		[16, 45, 15, 14, 46, 16],

		// 24
		[6, 147, 117, 4, 148, 118],
		[6, 73, 45, 14, 74, 46],
		[11, 54, 24, 16, 55, 25],
		[30, 46, 16, 2, 47, 17],

		// 25
		[8, 132, 106, 4, 133, 107],
		[8, 75, 47, 13, 76, 48],
		[7, 54, 24, 22, 55, 25],
		[22, 45, 15, 13, 46, 16],

		// 26
		[10, 142, 114, 2, 143, 115],
		[19, 74, 46, 4, 75, 47],
		[28, 50, 22, 6, 51, 23],
		[33, 46, 16, 4, 47, 17],

		// 27
		[8, 152, 122, 4, 153, 123],
		[22, 73, 45, 3, 74, 46],
		[8, 53, 23, 26, 54, 24],
		[12, 45, 15, 28, 46, 16],

		// 28
		[3, 147, 117, 10, 148, 118],
		[3, 73, 45, 23, 74, 46],
		[4, 54, 24, 31, 55, 25],
		[11, 45, 15, 31, 46, 16],

		// 29
		[7, 146, 116, 7, 147, 117],
		[21, 73, 45, 7, 74, 46],
		[1, 53, 23, 37, 54, 24],
		[19, 45, 15, 26, 46, 16],

		// 30
		[5, 145, 115, 10, 146, 116],
		[19, 75, 47, 10, 76, 48],
		[15, 54, 24, 25, 55, 25],
		[23, 45, 15, 25, 46, 16],

		// 31
		[13, 145, 115, 3, 146, 116],
		[2, 74, 46, 29, 75, 47],
		[42, 54, 24, 1, 55, 25],
		[23, 45, 15, 28, 46, 16],

		// 32
		[17, 145, 115],
		[10, 74, 46, 23, 75, 47],
		[10, 54, 24, 35, 55, 25],
		[19, 45, 15, 35, 46, 16],

		// 33
		[17, 145, 115, 1, 146, 116],
		[14, 74, 46, 21, 75, 47],
		[29, 54, 24, 19, 55, 25],
		[11, 45, 15, 46, 46, 16],

		// 34
		[13, 145, 115, 6, 146, 116],
		[14, 74, 46, 23, 75, 47],
		[44, 54, 24, 7, 55, 25],
		[59, 46, 16, 1, 47, 17],

		// 35
		[12, 151, 121, 7, 152, 122],
		[12, 75, 47, 26, 76, 48],
		[39, 54, 24, 14, 55, 25],
		[22, 45, 15, 41, 46, 16],

		// 36
		[6, 151, 121, 14, 152, 122],
		[6, 75, 47, 34, 76, 48],
		[46, 54, 24, 10, 55, 25],
		[2, 45, 15, 64, 46, 16],

		// 37
		[17, 152, 122, 4, 153, 123],
		[29, 74, 46, 14, 75, 47],
		[49, 54, 24, 10, 55, 25],
		[24, 45, 15, 46, 46, 16],

		// 38
		[4, 152, 122, 18, 153, 123],
		[13, 74, 46, 32, 75, 47],
		[48, 54, 24, 14, 55, 25],
		[42, 45, 15, 32, 46, 16],

		// 39
		[20, 147, 117, 4, 148, 118],
		[40, 75, 47, 7, 76, 48],
		[43, 54, 24, 22, 55, 25],
		[10, 45, 15, 67, 46, 16],

		// 40
		[19, 148, 118, 6, 149, 119],
		[18, 75, 47, 31, 76, 48],
		[34, 54, 24, 34, 55, 25],
		[20, 45, 15, 61, 46, 16]
	];

	QRRSBlock.getRSBlocks = function(typeNumber, errorCorrectLevel) {

		var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);

		if (rsBlock == undefined) {
			throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" +
				errorCorrectLevel);
		}

		var length = rsBlock.length / 3;

		var list = new Array();

		for (var i = 0; i < length; i++) {

			var count = rsBlock[i * 3 + 0];
			var totalCount = rsBlock[i * 3 + 1];
			var dataCount = rsBlock[i * 3 + 2];

			for (var j = 0; j < count; j++) {
				list.push(new QRRSBlock(totalCount, dataCount));
			}
		}

		return list;
	};

	QRRSBlock.getRsBlockTable = function(typeNumber, errorCorrectLevel) {

		switch (errorCorrectLevel) {
			case QRErrorCorrectLevel.L:
				return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
			case QRErrorCorrectLevel.M:
				return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
			case QRErrorCorrectLevel.Q:
				return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
			case QRErrorCorrectLevel.H:
				return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
			default:
				return undefined;
		}
	};

	//---------------------------------------------------------------------
	// QRBitBuffer
	//---------------------------------------------------------------------

	function QRBitBuffer() {
		this.buffer = new Array();
		this.length = 0;
	}

	QRBitBuffer.prototype = {

		get: function(index) {
			var bufIndex = Math.floor(index / 8);
			return (this.buffer[bufIndex] >>> 7 - index % 8 & 1) == 1;
		},

		put: function(num, length) {
			for (var i = 0; i < length; i++) {
				this.putBit((num >>> length - i - 1 & 1) == 1);
			}
		},

		getLengthInBits: function() {
			return this.length;
		},

		putBit: function(bit) {

			var bufIndex = Math.floor(this.length / 8);
			if (this.buffer.length <= bufIndex) {
				this.buffer.push(0);
			}

			if (bit) {
				this.buffer[bufIndex] |= 0x80 >>> this.length % 8;
			}

			this.length++;
		}
	};

	// support Chinese
	function utf16to8(str) {
		var out, i, len, c;
		out = '';
		len = str.length;
		for (i = 0; i < len; i++) {
			c = str.charCodeAt(i);
			if (c >= 0x0001 && c <= 0x007F) {
				out += str.charAt(i);
			} else if (c > 0x07FF) {
				out += String.fromCharCode(0xE0 | c >> 12 & 0x0F);
				out += String.fromCharCode(0x80 | c >> 6 & 0x3F);
				out += String.fromCharCode(0x80 | c >> 0 & 0x3F);
			} else {
				out += String.fromCharCode(0xC0 | c >> 6 & 0x1F);
				out += String.fromCharCode(0x80 | c >> 0 & 0x3F);
			}
		}
		return out;
	}

	function drawQrcode(options, debug) {
		options = options || {};
		options = extend(true, {
			canvasId: 'myQrcode',
			// canvas: canvas,
			text: '爱一个人就要勇敢说出来',
			x: 0,
			y: 0,
			width: 260,
			height: 260,
			padding: 20,
			// paddingColor: '#ffffff', // 默认与background一致
			typeNumber: -1,
			correctLevel: QRErrorCorrectLevel.H,
			background: '#ffffff',
			foreground: '#000000',
			image: {
				imageResource: '',
				width: 80,
				height: 80,
				round: true
			}
		}, options);

		if (!options.canvasId && !options.canvas) {
			console.warn('please set canvasId or canvas!');
			return;
		}

		if (!options.paddingColor) options.paddingColor = options.background;

		if (debug) {
			var qrcode = new QRCode(options.typeNumber, options.correctLevel);
			qrcode.addData(utf16to8(options.text));
			qrcode.make();

			return new Promise(function(resolve, reject) {
				resolve(qrcode);
			});
		} else {

			return new Promise(function(resolve, reject) {
				return resolve(createCanvas());
			});
		}

		function createCanvas() {
			// create the qrcode itself
			var qrcode = new QRCode(options.typeNumber, options.correctLevel);
			qrcode.addData(utf16to8(options.text));
			qrcode.make();

			const dpr = uni.getSystemInfoSync().pixelRatio;
			// 获取canvas实例 兼容拿不到canvas实例的情况;
			var ctx;
			var width;
			var canvas = options.canvas;
			if (canvas) {
				ctx = canvas.getContext('2d');
				canvas.width = options.width * dpr;
				canvas.height = options.width * dpr;
				width = options.width * dpr;
				// 背景色
				ctx.fillStyle = options.paddingColor;
				ctx.fillRect(0, 0, width + options.padding * 2, width + options.padding * 2);

				var tileW = (width - options.padding * 2) / qrcode.getModuleCount();
				var tileH = (width - options.padding * 2) / qrcode.getModuleCount();

				// 开始画二维码
				for (var row = 0; row < qrcode.getModuleCount(); row++) {
					for (var col = 0; col < qrcode.getModuleCount(); col++) {
						ctx.fillStyle = qrcode.isDark(row, col) ? options.foreground : options.background;
						var w = Math.ceil((col + 1) * tileW) - Math.floor(col * tileW);
						var h = Math.ceil((row + 1) * tileW) - Math.floor(row * tileW);
						ctx.fillRect(Math.round(col * tileW) + options.padding, Math.round(row * tileH) +
							options.padding, w, h);
					}
				}

				if (options.image.imageResource) {
					const imgW = options.image.width * dpr;
					const imgH = options.image.height * dpr;
					const dx = (width - imgW) / 2;
					const dy = (width - imgH) / 2;
					if (options.image.round) {
						// Logo边框
						const imgW2 = options.image.width * dpr + 30;
						const dx2 = (width - imgW2) / 2;
						const r2 = imgW2 / 2;
						const cx2 = dx2 + r2;
						ctx.beginPath();
						ctx.arc(cx2, cx2, r2, 0, 2 * Math.PI);
						ctx.fillStyle = '#ffffff';
						ctx.fill();
						ctx.restore();

						// 画Logo
						const r = imgW / 2;
						const cx = dx + r;
						const cy = dy + r;
						ctx.beginPath();
						ctx.arc(cx, cy, r, 0, 2 * Math.PI);
						ctx.clip();

						ctx.drawImage(options.image.imageResource, dx, dy, imgW, imgW);
						ctx.restore();
					} else {
						ctx.drawImage(options.image.imageResource, dx, dy, imgW, imgH);
						ctx.restore();
					}
				}

				return ctx;
			} else {
				// 拿不到实例时,创建canvas,返回canvasContext
				ctx = options._this ? uni.createCanvasContext && uni.createCanvasContext(options.canvasId,
					options._this) : uni.createCanvasContext && uni.createCanvasContext(options
					.canvasId);
				width = options.width;
				var tileW = (width - options.padding * 2) / qrcode.getModuleCount();
				var tileH = (width - options.padding * 2) / qrcode.getModuleCount();

				// draw in the canvas
				for (var row = 0; row < qrcode.getModuleCount(); row++) {
					for (var col = 0; col < qrcode.getModuleCount(); col++) {
						ctx.fillStyle = qrcode.isDark(row, col) ? options.foreground : options.background;
						var w = Math.ceil((col + 1) * tileW) - Math.floor(col * tileW);
						var h = Math.ceil((row + 1) * tileW) - Math.floor(row * tileW);
						ctx.fillRect(Math.round(col * tileW) + options.padding, Math.round(row * tileH) +
							options.padding, w, h);
					}
				}

				if (options.image.imageResource) {
					ctx.drawImage(options.image.imageResource, options.image.dx, options.image.dy, options
						.image.dWidth, options.image.dHeight);
				}

				ctx.draw(false, function(e) {
					options.callback && options.callback(e);
				});
			}

		}
	}

	return drawQrcode;

})));

Guess you like

Origin blog.csdn.net/qq_43148113/article/details/124816483