原本我是負責微信小程序的,後來看到項目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內部的邊框,可使得下載下來的二維碼更美觀,最終結果顯示以下: