canvas 繪製帶logo/文字二維碼

原本我是負責微信小程序的,後來看到項目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的二維碼

示例代碼微信小程序

如今弄懂了這個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內部的邊框,可使得下載下來的二維碼更美觀,最終結果顯示以下:

顯示文字的二維碼

示例代碼

相關文章
相關標籤/搜索