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

21 篇文章 0 订阅
6 篇文章 0 订阅

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

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

体验图片:

 

存在问题:

1、目前仅适用于pc端;

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

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

 

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

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

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的形式导出二维码,从而更方便计算小黑块的数目;

  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 = ['<table id="tableQR" style="border:0;border-collapse:collapse;">'];

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

				for (var col = 0; col < nCount; col++) {
					aHTML.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:' + nWidth + 'px;height:' + nHeight + 'px;background-color:' + (oQRCode.isDark(row, col) ? _htOption.colorDark : _htOption.colorLight) + ';"></td>');
				}

				aHTML.push('</tr>');
			}

			aHTML.push('</table>');
			_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、随机填充小黑块;

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

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

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

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

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

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

 // 统计方块反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")
        },

统计田的方法:

  // 统计田
        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个方块的横竖方法类似,所以我把她们合并成一个方法来分别调用:

       / *返回判断条件;当数量为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个小方块;

====》

    // 绘制艺术二维码
        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);
            };
        },

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

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

 

拓展:

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

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

html:

<div id="capture" style="padding: 10px; background: #f5da55">
    <h4 style="color: #000; ">Hello world!</h4>
</div>

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 = $("<a>").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 width="320" height="180" style="width:160px;height:90px;"></canvas>

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

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

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 使用qrcode.js库可以轻松实现二维码的生成,所需的步骤如下:1. 安装qrcode.js库,可以使用NPM或者yarn进行安装;2. 创建一个HTML文件,在里面引入qrcode.js库;3. 使用qrcode.js库提供的API,创建一个新的QR码对象;4. 使用QR码对象生成一个新的二维码。 ### 回答2: qrcode.js是一个用于生成二维码的JavaScript库,可以通过以下步骤来实现二维码的生成: 1. 引入qrcode.js库:从qrcode.js的官方网站下载最新版本的库文件,然后在HTML文件中使用`<script>`标签将其引入。 2. 创建一个canvas元素:使用HTML的`<canvas>`标签创建一个用于显示二维码的画布。 3. 实例化qrcode对象:在JavaScript中,使用`new QRCode()`来实例化一个qrcode对象。 4. 设置二维码的参数:使用qrcode对象的`makeCode()`方法来设置二维码的参数,如二维码的内容、大小、错误校验级别等。例如,`qrcode.makeCode("Hello, World!")`会生成一个包含"Hello, World!"内容的二维码。 5. 将二维码渲染到canvas中:使用qrcode对象的`toCanvas()`方法将生成的二维码渲染到之前创建的canvas元素中。例如,`qrcode.toCanvas(document.getElementById("canvas"))`会将二维码渲染到id为"canvas"的元素中。 6. 样式和交互:根据需要,可以使用CSS样式和JavaScript事件来对生成的二维码进行美化和交互操作。例如,可以设置canvas元素的宽高和边框样式,也可以为二维码添加点击事件等。 7. 最后,将HTML文件在浏览器中打开,即可看到生成的二维码。 总结:使用qrcode.js库可以轻松实现二维码的生成。只需引入库文件,创建canvas元素,实例化qrcode对象并设置参数,然后将二维码渲染到canvas中即可。通过样式和交互操作,可以对生成的二维码进行个性化处理。 ### 回答3: qrcode.js是一个用于生成二维码的JavaScript库。使用qrcode.js生成二维码需要以下步骤: 1. 在HTML文件中引入qrcode.js库。可以使用CDN链接或者本地文件路径引入。 2. 创建一个canvas元素来呈现二维码。可以在HTML中定义一个canvas标签,也可以使用JavaScript动态创建一个canvas元素。 3. 使用JavaScript代码初始化qrcode.js库,并设置一些配置选项。比如,选择要显示的二维码版本,纠错级别,和尺寸。 4. 调用qrcode.js库的makeCode方法,将要生成的文本或URL作为参数传入。这个方法会在canvas元素中生成二维码图像。 5. 按需求对二维码进行自定义样式的修改,比如修改颜色、添加logo等。 6. 最后,将生成的二维码展示给用户。 下面是一个简单的示例代码: ``` <!DOCTYPE html> <html> <head> <title>生成二维码</title> <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdn.staticfile.org/jquery.qrcode/1.0/jquery.qrcode.min.js"></script> </head> <body> <canvas id="qrcodeCanvas"></canvas> <script> // 创建canvas元素 var canvas = document.getElementById("qrcodeCanvas"); // 设置qrcode.js的配置选项 var options = { text: "https://www.example.com", // 要生成二维码的文本或URL width: 256, // 二维码宽度 height: 256 // 二维码高度 }; // 初始化qrcode.js库 var qrcode = new QRCode(canvas, options); </script> </body> </html> ``` 通过上述步骤,您就可以使用qrcode.js库来生成二维码了。根据需要,您还可以通过修改配置选项、自定义样式等进行额外的定制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值