前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js

生成艺术二维码的体验地址::http://yating.online/demo/qr/

喜欢的话,给个星吧~https://github.com/Chenyating/ART-QR-Code

体验图片:

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第1张图片

 

存在问题:

1、目前仅适用于pc端;

2、内容过于复杂,可能无法识别出来;

3、有的二维码,用手机可以扫描出来,但是无法图片识别;

 

现在讲一下大概开发的过程:

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第2张图片

用的数组比较多;主要都是用来记录小黑块;

array记录小黑的行列上色;

依次类推;

1、生成二维码,需要使用到QRCode.js:

QRCode.js 是一个用于生成二维码的 JavaScript 库。主要是通过获取 DOM 的标签,再通过 HTML5 Canvas 绘制而成,不依赖任何库。

基本的用法是:

var qrcode = new QRCode("test", {
    text: "www.yating.online",
    width: 128,
    height: 128,
    colorDark : "#000000",
    colorLight : "#ffffff",
    correctLevel : QRCode.CorrectLevel.H
});

Github 地址:https://github.com/davidshimjs/qrcodejs

绘制二维码以后需要使用回调函数的方法:

 // html
  
  var qrcode = new QRCode(document.getElementById("qrcode"), {
     text: "www.yating.online",
    width: 128,
    height: 128,
    colorDark : "#000000",
    colorLight : "#ffffff",
    correctLevel : QRCode.CorrectLevel.H
  },function(src){
    console.log(src)
    //回调方法
 });

因为最后导出的是以canvas的形式导出的二维码,很不方便我计算小黑快;

所以我在源码上面,改成最后以table的形式导出二维码,从而更方便计算小黑块的数目;

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第3张图片

  1. 在源码中找到这个方法:Drawing.prototype.draw = function (oQRCode)
  2. 把里面的内容删掉替换成现有的导出table的方法,代码如下:
var _htOption = this._htOption;
			var _el = this._el;
			var nCount = oQRCode.getModuleCount();
			var nWidth = Math.floor(_htOption.width / nCount);
			var nHeight = Math.floor(_htOption.height / nCount);
			var aHTML = [''];

			for (var row = 0; row < nCount; row++) {
				aHTML.push('');

				for (var col = 0; col < nCount; col++) {
					aHTML.push('');
				}

				aHTML.push('');
			}

			aHTML.push('
'); _el.innerHTML = aHTML.join(''); // Fix the margin values as real size. var elTable = _el.childNodes[0]; var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2; var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2; if (nLeftMarginTable > 0 && nTopMarginTable > 0) { elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px"; } this._bIsPainted = true;

 

2、计算二维码的小黑快;

通过得到tr的行数,我们就可以知道这个二维码是一个n*n的正方形;

然后在每个小方块的位置,记录在一个数组里。并且记录好该方块是否上色;

主要是接下来给二维码绘制图案起作用;

在最一开始的时候,我要先把3个码眼给保存下来,以后就不在对码眼区域的小黑块做统计了

  //    首先把3个大框框保存下来;
                    if ((i < 7 && j < 7) || (i > this.arrayLength - 8 && j < 8) || (i < 8 && j > this.arrayLength - 8)) {
                        this.array[i][j][1] = 1;
                    }

array[i][j][0]=1:表示第i行j列的小方块是黑色的;

array[i][j][1]=1:表示第i行j列的小方块待会不做记录处理;

以此类推:

countWidth() {
            var tr = $("#tableQR tbody").find("tr");
            // 获取二维码一行的数量,从而得知是n*n个;设置一个n行n列的数组
            this.arrayLength = tr.length;
            // 开始遍历表格,把每一个td的上色情况存在array数组里;
            this.array = [];
            for (let i = 0; i < tr.length; i++) {
                this.array[i] = [];
                // 首先遍历tr数组
                // console.log("第", i, "行")
                for (let j = 0; j < tr[i].children.length; j++) {
                    this.array[i][j] = []; //第一个是上色情况,第二个是记录情况
                    // 遍历每个tr里的td,记录每个td的上色情况;
                    if (tr[i].children[j].style.backgroundColor == 'black') {
                        this.array[i][j][0] = 1;
                        this.array[i][j][1] = 0;
                        // this.array[i][j]= 1;
                    } else {
                        this.array[i][j][0] = 0;
                        this.array[i][j][1] = 1;
                        // this.array[i][j] = 0;
                    }
                    //    首先把3个大框框保存下来;
                    if ((i < 7 && j < 7) || (i > this.arrayLength - 8 && j < 8) || (i < 8 && j > this.arrayLength - 8)) {
                        this.array[i][j][1] = 1;
                    }
                }
            }
        },

3、随机填充小黑块;

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第4张图片

最主要的精髓就是,填充小黑块的正中心,大概1/9分布到所有小黑块上去,就可以识别了

众所周知,只要把有颜色的图案,替换掉小黑块就可以识别出来了。

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第5张图片

所以。我给它设计了10种不同的填充方式:

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第6张图片

他们分别对应的小黑块是这个样子的:

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第7张图片

4、所以我们就要开始随机的去计算这10种可能的图案能把所有小黑快填满的数据;

每次生成的组成都是不一样的图案,给大家看一下例子;我把第一张图叫正7图案,因为看起来像7;第二张图叫反7;

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第8张图片

 // 统计方块反7正7的个数
        count7() {
            var positive7 = 0;
            this.arrayPositive7 = [];
            var reverse7 = 0;
            this.arrayRever7 = [];
            for (let i = 0; i < this.arrayLength - 2; i++) {
                //   遍历每一个数组里的值
                for (let j = 0; j < this.arrayLength - 2; j++) {
                    // 如果这个小方块没有上色或者这个小方块被记录过了,那么我们就不用管它了!
                    if (this.array[i][j][1] == 1) {
                        continue;
                    } else {
                        // 随机记录正反7
                        if (parseInt(Math.random() * 2) == 1) {
                            // 判断是否超出;
                            // 否则判断他是否是正7。
                            if (this.array[i][j][0] == 1 &&
                                this.array[i][j][1] == 0 &&
                                this.array[i + 1][j][0] == 0 &&
                                this.array[i][j + 1][0] == 1 &&
                                this.array[i][j + 1][1] == 0 &&
                                this.array[i + 1][j + 1][0] == 1 &&
                                this.array[i + 1][j + 1][1] == 0) {
                                // 现在positive7已经被记录了;
                                this.array[i][j][1] = 1
                                this.array[i][j + 1][1] = 1
                                this.array[i + 1][j + 1][1] = 1
                                // 把竖7的i,j记录进去;
                                // 开始收收集7行的小方块数据;
                                this.arrayPositive7[positive7] = [];
                                this.arrayPositive7[positive7][0] = j;
                                this.arrayPositive7[positive7][1] = i
                                positive7 = positive7 + 1;
                            } else {
                                continue;
                            }
                        } else {
                            // 反7
                            if (this.array[i][j][0] == 1 &&
                                this.array[i][j][1] == 0 &&
                                this.array[i + 1][j + 1][0] == 0 &&
                                this.array[i][j + 1][0] == 1 &&
                                this.array[i][j + 1][1] == 0 &&
                                this.array[i + 1][j][0] == 1 &&
                                this.array[i + 1][j][1] == 0) {
                                // 现在positive7已经被记录了;
                                this.array[i][j][1] = 1
                                this.array[i][j + 1][1] = 1
                                this.array[i + 1][j][1] = 1
                                // 把竖7的i,j记录进去;
                                // 开始收收集7行的小方块数据;
                                this.arrayRever7[reverse7] = [];
                                this.arrayRever7[reverse7][0] = j;
                                this.arrayRever7[reverse7][1] = i
                                reverse7 = reverse7 + 1;
                            } else {
                                continue;
                            }
                            // 开始收收集7行的小方块数据;
                            // 否则判断他是否是横7。
                        }
                    }
                }
            }
            // console.log(this.arrayPositive7);
            // console.log(this.arrayRever7);
            this.pain(this.arrayRever7, 2, 2, 7, "re")
            this.pain(this.arrayPositive7, 2, 2, 7, "po")
        },

统计田的方法:

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第9张图片

  // 统计田
        countTian() {
            var tian = 0;
            this.arrayTian = [];
            for (let i = 0; i < this.arrayLength - 2; i++) {
                //   遍历每一个数组里的值
                for (let j = 0; j < this.arrayLength - 2; j++) {
                    // 如果这个小方块没有上色或者这个小方块被记录过了,那么我们就不用管它了!
                    if (this.array[i][j][1] == 1) {
                        continue;
                    } else {
                        if (
                            this.array[i][j][0] == 1 &&
                            this.array[i][j][1] == 0 &&
                            this.array[i + 1][j][0] == 1 &&
                            this.array[i + 1][j][1] == 0 &&
                            this.array[i][j + 1][0] == 1 &&
                            this.array[i][j + 1][1] == 0 &&
                            this.array[i + 1][j + 1][0] == 1 &&
                            this.array[i + 1][j + 1][1] == 0) {
                            // 现在positive7已经被记录了;
                            this.array[i][j][1] = 1
                            this.array[i + 1][j][1] = 1
                            this.array[i][j + 1][1] = 1
                            this.array[i + 1][j + 1][1] = 1
                            // 把竖7的i,j记录进去;
                            // 开始收收集7行的小方块数据;
                            this.arrayTian[tian] = [];
                            this.arrayTian[tian][0] = j;
                            this.arrayTian[tian][1] = i
                            tian = tian + 1;
                        } else {
                            continue;
                        }
                    }
                }
            }
            this.pain(this.arrayTian, 2, 2, 22, "tian");
        },

因为统计:4、3、2个方块的横竖方法类似,所以我把她们合并成一个方法来分别调用:

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第10张图片前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第11张图片

       / *返回判断条件;当数量为4、3、2个方块的;
         * 类型有:col和row;
         * i,j此时遍历的条件;
         */
        countType(arrayType, num) {
            var col = 0;
            this[arrayType].col = [];
            var row = 0;
            this[arrayType].row = [];
            for (let i = 0; i < this.arrayLength; i++) {
                //   遍历每一个数组里的值
                for (let j = 0; j < this.arrayLength; j++) {
                    // 如果这个小方块没有上色或者这个小方块被记录过了,那么我们就不用管它了!
                    if (this.array[i][j][1] == 1) {
                        continue;
                    } else {
                        // 随机记录行竖4
                        if (parseInt(Math.random() * 2) == 1) {
                            // 判断是否超出;
                            if (i >= this.arrayLength - num) {
                                continue;
                            } else {
                                // 否则判断他是否是竖4。
                                if (this.condition(num, "col", i, j)) {
                                    // 现在col4已经被记录了;
                                    for (k = 0; k < num; k++) {
                                        this.array[i + k][j][1] = 1
                                    }
                                    // 把竖4的i,j记录进去;
                                    // 开始收收集4行的小方块数据;
                                    this[arrayType].col[col] = [];
                                    this[arrayType].col[col][0] = j;
                                    this[arrayType].col[col][1] = i
                                    col = col + 1;
                                } else {
                                    continue;
                                }
                            }
                        } else {
                            if (j >= this.arrayLength - num) {
                                continue;
                            } else {

                                if (this.condition(num, "row", i, j)) {
                                    // 现在row4已经被记录了;
                                    for (k = 0; k < num; k++) {
                                        this.array[i][j + k][1] = 1
                                    }
                                    // 把横4的i,j记录进去;
                                    this[arrayType].row[row] = [];
                                    this[arrayType].row[row][0] = j;
                                    this[arrayType].row[row][1] = i
                                    row = row + 1;
                                } else {
                                    continue;
                                }
                            }
                        }
                    }
                }
            }
            this.pain(this[arrayType].row, num, 1, num, "row");
            this.pain(this[arrayType].col, 1, num, num, "col");
        },

if(condition())会返回判断该类型图片的条件

     /*
         * 返回判断条件;当数量为4、3、2个方块的;
         * 类型有:col和row;
         * i,j此时遍历的条件;
         */
        condition(num, type, i, j) {
            if (num == 4 && type == "row") {
                return (this.array[i][j][0] == 1 &&
                    this.array[i][j][1] == 0 &&
                    this.array[i][j + 1][0] == 1 &&
                    this.array[i][j + 1][1] == 0 &&
                    this.array[i][j + 2][0] == 1 &&
                    this.array[i][j + 2][1] == 0 &&
                    this.array[i][j + 3][0] == 1 &&
                    this.array[i][j + 3][1] == 0)
            }
            if (num == 4 && type == "col") {
                return (this.array[i][j][0] == 1 &&
                    this.array[i][j][1] == 0 &&
                    this.array[i + 1][j][0] == 1 &&
                    this.array[i + 1][j][1] == 0 &&
                    this.array[i + 2][j][0] == 1 &&
                    this.array[i + 2][j][1] == 0 &&
                    this.array[i + 3][j][0] == 1 &&
                    this.array[i + 3][j][1] == 0)
            }
            if (num == 3 && type == "row") {
                return (this.array[i][j][0] == 1 &&
                    this.array[i][j][1] == 0 &&
                    this.array[i][j + 1][0] == 1 &&
                    this.array[i][j + 1][1] == 0 &&
                    this.array[i][j + 2][0] == 1 &&
                    this.array[i][j + 2][1] == 0)
            }
            if (num == 3 && type == "col") {
                return (this.array[i][j][0] == 1 &&
                    this.array[i][j][1] == 0 &&
                    this.array[i + 1][j][0] == 1 &&
                    this.array[i + 1][j][1] == 0 &&
                    this.array[i + 2][j][0] == 1 &&
                    this.array[i + 2][j][1] == 0)
            }
            if (num == 2 && type == "row") {
                return (this.array[i][j][0] == 1 &&
                    this.array[i][j][1] == 0 &&
                    this.array[i][j + 1][0] == 1 &&
                    this.array[i][j + 1][1] == 0)
            }
            if (num == 2 && type == "col") {
                return (this.array[i][j][0] == 1 &&
                    this.array[i][j][1] == 0 &&
                    this.array[i + 1][j][0] == 1 &&
                    this.array[i + 1][j][1] == 0)
            }
        },

最后没有填满的小黑块都均以1个小黑块来记录下来:

  // 统计剩余1单个方块
        count1() {
            var col1 = 0;
            this.array1 = [];
            for (let i = 0; i < this.arrayLength; i++) {
                //   遍历每一个数组里的值
                for (let j = 0; j < this.arrayLength; j++) {
                    // 如果这个小方块没有上色或者这个小方块被记录过了,那么我们就不用管它了!
                    if (this.array[i][j][1] == 1) {
                        continue;
                    } else {
                        this.array[i][j][1] = 1
                        // 剩下的按单个记录保存;
                        this.array1[col1] = [];
                        this.array1[col1][0] = j;
                        this.array1[col1][1] = i
                        col1 = col1 + 1;
                    }
                }
            }
            this.pain(this.array1, 1, 1, 1, "one");
        },

5、开始绘制图案

每次计算好不同类型的图案的方式,紧接着我就会开始绘制该图案到canvas上:

因为i行j列,对应x、y轴时;

i对应y;

j对应x;

所以渲染的时候要注意一下以免搞混杂了。

   // 绘制艺术二维码
        pain(arrayName, width, height, num, type) {
            var img = new Image();
            img.src = this.imgSrc(num, type);
            //浏览器加载图片完毕后再绘制图片
            img.onload = () => {
                //以Canvas画布上的坐标(10,10)为起始点,绘制图像 //图像的宽度和高度分别缩放到350px和100px
                if (arrayName.length != 0) {
                    for (let i = 0; i < arrayName.length; i++) {
                        this.canvasBg.drawImage(img, arrayName[i][0] * this.unit, arrayName[i][1] * this.unit, width * this.unit, height * this.unit);
                    }
                } else {
                    return;
                }
            };
        },

this.imgSrc(num,type)会根据不同的图案类型返回图片路径

    // 返回图片src
        imgSrc(num, type) {
            if (num == 4 && type == "row") {
                return "../img/qr/row4.png";
            }
            if (num == 4 && type == "col") {
                return "../img/qr/col4.png";
            }
            if (num == 3 && type == "row") {
                return "../img/qr/row3.png";
            }
            if (num == 3 && type == "col") {
                return "../img/qr/col3.png";
            }
            if (num == 7 && type == "re") {
                return "../img/qr/re7.png";
            }
            if (num == 7 && type == "po") {
                return "../img/qr/po7.png";
            }
            if (num == 22 && type == "tian") {
                return "../img/qr/tian.png";
            }
            if (num == 2 && type == "row") {
                return "../img/qr/row2.png";
            }
            if (num == 2 && type == "col") {
                return "../img/qr/col2.png";
            }
            if (num == 1 && type == "one") {
                return "../img/qr/one.png";
            }
        },

6、绘制码眼

最后我们开始绘制定位的码眼:它的宽度是7个小方块;

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第12张图片====》前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第13张图片

    // 绘制艺术二维码
        painEye() {
            var img = new Image();
            img.src = "../img/qr/eye.png";
            //浏览器加载图片完毕后再绘制图片
            img.onload = () => {
                this.canvasBg.drawImage(img, 0 * this.unit, 0 * this.unit, 7 * this.unit, 7 * this.unit);
                this.canvasBg.drawImage(img, (this.arrayLength - 7) * this.unit, 0 * this.unit, 7 * this.unit, 7 * this.unit);
                this.canvasBg.drawImage(img, 0 * this.unit, (this.arrayLength - 7) * this.unit, 7 * this.unit, 7 * this.unit);
            };
        },

基本上到最后你就能生成一个这样的二维码了:

前端:艺术二维码生成过程canvas+html2canvas.js+qrcode.js_第14张图片

然后我们再给它导出图片格式;就万事大吉了。

 

拓展:

1、因为我还会加上一个背景,但是我的背景是img标签;如果你想要div截图成img输出;

可以使用html2canvas:http://html2canvas.hertzen.com/

html:

Hello world!

js:

html2canvas(document.querySelector("#capture")).then(canvas => {
    document.body.appendChild(canvas)
});

解决html标签导出img不清晰的问题:

      toBeCanvas() {
            var copyDom = $("#canvasQR");
            var width = copyDom.offsetWidth; //dom宽
            var height = copyDom.offsetHeight; //dom高
            var scale = 2; //放大倍数
            html2canvas(this.$refs['order'], {
                dpi: window.devicePixelRatio * 2,
                scale: scale,
                width: width,
                heigth: height,
            }).then(canvas => {
                const context = canvas.getContext('2d');
                // 【重要】关闭抗锯齿 https://segmentfault.com/a/1190000011478657
                context.mozImageSmoothingEnabled = false;
                context.webkitImageSmoothingEnabled = false;
                context.msImageSmoothingEnabled = false;
                context.imageSmoothingEnabled = false;
                var url = canvas.toDataURL();
                var triggerDownload = $("").attr("href", url).attr("download", "详情.png").appendTo("body");
                triggerDownload[0].click();
                triggerDownload.remove();
            });
        },

这四行是关键:

                context.mozImageSmoothingEnabled = false;
                context.webkitImageSmoothingEnabled = false;
                context.msImageSmoothingEnabled = false;
                context.imageSmoothingEnabled = false;

2、img绘制到canvas不清晰的解决方法:

canvas的内容放大到两倍,以img的形式展示1倍;

绘制到canvas的图片绝对清晰;

你可能感兴趣的:(javascript,心得,工具)