原本我是负责微信小程序的,后来看到项目web端生成的二维码比较单调,就决定在二维码中间加一个 logo,因而就在网上处处找现成的源码,后来终于在github上找到了一份能够在二维码中间放logo的qrcode 库。结果后来boss提了需求,想在中间显示文字,而不是图片,这下尴尬了,这个需求有点奇葩,怕是找不 到源码了,因而无奈之下我决定深刻这个二维码生成库的内部,去了解一下这个东东到底是怎么画出来的。 首先是研究一下这个logo是如何画出来的。因为我自己没有太多web开发经验,尤为没有研究过 jquery,因此对于这个库的架构仍是很蒙蔽的,可是多少仍是在微信小程序开发中用了js,因此哪怕框架结 构看的不是很清晰,但每一个函数的做用仍是能够看懂的。根据关键字imgWidth定位到这样一个函数:jquery
var QRMode, QRErrorCorrectLevel, QRMaskPattern, QRUtil, QRMath, i; for (function (a) { a.fn.qrcode = function (b) { var c, d; return "string" == typeof b && (b = { text: b }), b = a.extend({}, { width: 256, height: 256, cheight: b.height + 50, cwidth: b.width, cstyle:"width:"+b.cwidth/4+"px;height:"+b.cheight/4+"px;", content:"", imgWidth: b.width / 2.5, imgHeight: b.height / 2.5, typeNumber: -1, correctLevel: QRErrorCorrectLevel.H, background: "#ffffff", foreground: "#000000"
说实话,我对这样的结构真的好无头绪,看起来好像是匿名函数,可是直接一个for是个什么鬼?这代码是我根据压缩代码直接恢复的,只能解释为可能这是个完整的for语句,只不过for里放了太多东西致使后面的逻辑在很远的地方。可是先无论这些,个人目的是查找图片绘制的逻辑,继续往下看:git
c = function () { var c, d, e, f, g, h, i, j, k, a = new QRCode(b.typeNumber, b.correctLevel); for (a.addData(b.text), a.make(), c = document.createElement("canvas"), c.width = b.cwidth, c.height = b.cheight,c.style=b.cstyle, d = c.getContext("2d"), b.src && (e = new Image(), e.src = b.src, e.onload = function () { d.drawImage(e, (b.width - b.imgWidth) / 2, (b.height - b.imgHeight) / 2, b.imgWidth, b.imgHeight) }), f = b.width / a.getModuleCount(), g = b.height / a.getModuleCount(), h = 0; h < a.getModuleCount(); h++) { for (i = 0; i < a.getModuleCount(); i++) { d.fillStyle = a.isDark(h, i) ? b.foreground : b.background, j = Math.ceil((i + 1) * f) - Math.floor(i * f), k = Math.ceil((h + 1) * f) - Math.floor(h * f), d.fillRect(Math.round(i * f), Math.round(h * g), j, k) } } return c }
在下面发现这样一个名叫c的函数,不得不说,压缩的代码命名方式果真简单暴力,全是单个字母的。可是幸亏这个函数我是读得懂的。首先是生成一个QRCode的对象,而且用解构赋值的方式将返回值保存在c, d, e, f, g, h, i, j, k, a几个变量中,具体干吗的我就先无论,专心往下看图片的部分。 接下来的一行是一个for循环语句,可是在for内部的第一部分进行了大量的赋值操做以及初始化操做,甚至生成了一个Image的对象保存在了e变量中,而且对e的图片资源进行了赋值以及绘制图片的操做也都完成了:github
e = new Image(), e.src = b.src, e.onload = function () { d.drawImage(e, (b.width - b.imgWidth) / 2, (b.height - b.imgHeight) / 2, b.imgWidth, b.imgHeight) }
看到这里部分不惊叹压缩过的代码的精悍,短短一个for语句竟然能够作这么多事。其中这里有一个d变量, 这个d就是获取的canvas的contex,这部分代码也是在这个for语句第一部分里:web
d = c.getContext("2d")
因而就这么神奇的完成了对logo的绘制。可是既然看到了这里,我不如直接看看二维码自己是如何绘制的好 了,因而继续往下读:canvas
for (i = 0; i < a.getModuleCount(); i++) { d.fillStyle = a.isDark(h, i) ? b.foreground : b.background, j = Math.ceil((i + 1) * f) - Math.floor(i * f), k = Math.ceil((h + 1) * f) - Math.floor(h * f), d.fillRect(Math.round(i * f), Math.round(h * g), j, k); }
这就是以前绘制图片那个for循环内部的逻辑,又是一个循环,可是这个循环就正常多了,没有那么花哨的组合,能够看到这里用到了上一层循环中初始化的 f,g三个变量,这里分别是计算出来的须要绘制的二维码的每个小块的宽度和高度,总体的两个for循环组合起来,相似于咱们的打字机同样,一行一行的扫描保存了二维码信息的数组a,并一个小块一个小块的绘制在canvas上,至于这个a是如何计算的,这就属于二维码计算的逻辑了,就算我懂了也改不了什么,就不看了。 这里是logo绘制的展现图:小程序
示例代码微信小程序
如今弄懂了这个logo绘制的逻辑,因而开始把这个image绘制改成文字绘制。 首先删掉外层for循环中图片绘制部分:数组
for (a.addData(b.text), a.make(), c = document.createElement("canvas"), c.width = b.cwidth, c.height = b.cheight, c.style = b.cstyle, d = c.getContext("2d"), d.fillStyle = b.background, d.fillRect(0, 0, b.cwidth, b.cheight), f = b.width / a.getModuleCount(), g = b.height / a.getModuleCount(), h = 0; h < a.getModuleCount(); h++) { for (i = 0; i < a.getModuleCount(); i++) { d.fillStyle = a.isDark(h, i) ? b.foreground : b.background, j = Math.ceil((i + 1) * f) - Math.floor(i * f), k = Math.ceil((h + 1) * f) - Math.floor(h * f), d.fillRect(Math.round(i * f + b.border), Math.round(h * g + b.border), j, k) } }
而后在canvas中绘制一个白色的不透明圆形做为文字的背景,固然若是你喜欢绿色,也是能够的。微信
d.beginPath(); d.strokeStyle = "white"; d.arc(b.width / 2 + b.border, b.height / 2 + b.border, 35, 0, Math.PI * 2, false); d.closePath(); d.fillStyle = 'white'; d.fill();
在外部传参部分添加了用于接收须要传入文字的变量type,这样在调用qrcode的时候就能够经过修改传入的type字段控制二维码显示的文字信息,绘制中间文字的代码以下:架构
d.font = "normal 35px arial"; //设置字体颜色 d.fillStyle = "red"; d.moveTo(d.width / 2 + b.border, 20); d.lineTo(d.width / 2 + b.border, 50); d.stroke(); d.textAlign = "center" d.fillText(b.type, Math.floor(b.width / 2 + b.border), Math.floor(b.height / 2 +b.border +12));
文字的显示我使用的是红色,没有特殊缘由,骚气。 除此以外我在二维码周边也留下了边框,这样的话显示二维码的时候就不会与周围的空间显得太拥挤,尤为是经过绘制在canvas内部的边框,可使得下载下来的二维码更美观,最终结果显示以下: