網絡上已經有很是多的二維碼編碼和解碼工具和代碼,不少都是服務器端的,也就是說須要一臺服務器才能提供二維碼的生成。本着對服務器性能的考慮,這種小事情都讓服務器去作,感受對不住服務器,尤爲是對於大流量的網站,雖然有服務器端緩存,畢竟須要大量的CPU運算時間,這或多或少也是很大的一塊壓力。因此就想,有沒有一種不靠服務器,就只靠JS就生成二維碼呢,畢竟二維碼就是一堆黑白點而已。我也沒有刻意去找網絡上是否已經存在這樣的解決方案,並且本身一直想深刻分析二維碼的生成細節,現有的項目也有這樣的需求,因而我本身研究了下,寫下了這麼個qr.js。javascript
你們能夠從這個地址下載:http://files.cnblogs.com/JerryWeng/qr.jscss
先看看這個東西的效果:html
它有兩種輸出模式:java
第一種是直接經過<img>對於base64的支持,把二維碼數據轉成一個bmp編碼的base64數據字符串做爲<img>的src:jquery
第二種是把每一個點作成一個div,而後經過css變成一個黑白點的矩陣算法
這是測試的HTML代碼:shell
<!DOCTYPE html> <html> <head> <script src="./jquery-1.11.1.min.js" type="text/javascript"></script> <script src="./qr.js" type="text/javascript"></script> <script type="text/javascript"> var qr_coder = null; $(document).ready(function(){ qr_coder = new QRCoder($('#qr_container')); $('#qr_gen').click(function() { $('#qr_container').html("generating"); var watch_start=new Date(); qr_coder.setMode(1); qr_coder.draw( $('#qr_link').val(), $("[name='qr_capacity']:checked").val(), 'icon.png', function(data) { var watch_end=new Date(); console.log("cost:"+(watch_end-watch_start)+"ms"); }); }); }); </script> </head> <body> <h1>QR CODER</h1> <div style="margin:auto; position:relative; margin-left: 50%; left: -250px; width:500px;"> <label for="qr_link">URL:</label> <input id="qr_link" type="text" value="http://you.ctrip.com" style="width:350px;" /> <button id="qr_gen" value="Generate">Generate</button> <br /> <div style="display:none"> <input id="qr_capacity_l" name="qr_capacity" type="radio" value="L"/> <label for="qr_capacity_l">7%</label> <input id="qr_capacity_m" name="qr_capacity" type="radio" value="M"/> <label for="qr_capacity_m">15%</label> <input id="qr_capacity_q" name="qr_capacity" type="radio" value="Q"/> <label for="qr_capacity_q">25%</label> <input id="qr_capacity_h" name="qr_capacity" type="radio" value="H" checked/> <label for="qr_capacity_h">30%</label> </div> </div> <div id="qr_container" style="margin:auto; position:relative;"></div> </body> </html>
在IE6,7,8,9,10,Firefox,Chrome中測試經過。數組
若是對於實現細節感興趣,下面我來詳細說明如何實現。瀏覽器
1、參考文檔緩存
在開始以前,須要準備一些參考文檔來幫助理解:
1, QR 國際標準 ISO/IEC 18004. (http://raidenii.net/files/datasheets/misc/qr_code.pdf)
2, http://coolshell.cn/articles/10590.html
3, Galois Field 伽羅華域 (參考度娘)
4, Reed Solomon 糾錯編碼 (參考度娘)
5, Bitmap 編碼規範 (http://zh.wikipedia.org/wiki/Bitmap)
6, Base64 編碼 (參考度娘)
2、流程
http://www.processon.com/view/link/537c20340cf27a0d78936e61
整個流程,步驟有點多,但其實並不複雜,其中大多數步驟在標準規範中已經說明,在參考文檔2中,他已經把編碼部分說的很是詳細,我就很少贅述了,我在下面補充說下一些比較搞的概念。
3、說明
首先是伽羅華域,QR的糾錯編碼都是基於GF(256)的,GF的最大特性是它的封閉性,不管是加減乘除,它計算結果始終落在這個有限域中,而且GF256中的任何一個元素,均可以用GF2的組合來表示,也就是0,1表示,咱們經過1+x^1+x^2+...+x^n這樣的多項式來表示一個這個有限域中的數,其實,咱們不用在乎這裏的x,咱們只關心這個多項式的係數組合,每一個x的指數表明係數所佔的位數,好比x^8+x^6+x+1就對應二進制10100011,因此其實都是二進制的運算。GF256一共就256個數,咱們能夠生成好,而後以數組和哈希表的形式來參與計算,具體如何生成GF256的,你們能夠參考下這篇wiki,http://en.wikipedia.org/wiki/Finite_field_arithmetic
而後是RS糾錯編碼,RS編碼都是基於GF256的,因此,咱們須要先熟悉GF256的運算方法,RS編碼說簡單了,就是首先知道我須要有多少個糾錯的codeblock,而後以這個數構造一個生成多項式:(x-a^0)(x-a^1)...(x-a^n-1),這裏的a,或叫alpha,就是GF256裏的底數,a^n-1表明一個GF256有限域中元素,這裏的n就是糾錯codeblock的個數,而後把要編碼的數據codeblocks組成一個相似的多項式,每一個codeblock的值就是多項式的係數,從高位到低位排列,用這個數據多項式除以生成多項式,而後取餘數,這個餘數也應該是在GF256裏的數,其實就是手工法取餘,這些運算方法在GF的那篇wiki裏也有說明,詳細也可參見這篇wiki:http://en.wikipedia.org/wiki/Reed–Solomon_error_correction
再說下mask的問題,最後編碼後的數據,爲了可以儘可能地分散黑點和白點的分佈,便於掃描器掃描,須要每一個數據位與某種mask作XOR,爲何不是固定的mask呢,由於無法用一種mask分散全部的編碼。規範中列舉了8種mask函數,這些函數,只要符合,就返回1,不然是0,而後每一個對應的數據位(x,y)代入這個函數,而後再和相應的數據位XOR,這裏的x表明列號,y表明行號,左上角是0點,規範中的i表明的是行號,j表明的是列號,這點要注意。而後咱們要從8個mask函數中選擇一個最合適的,選擇方法是分別和4種決策方法並根據其權重計算一個分數並求和,選取這個得分最低的mask就是咱們要用的mask。這4種決策方法和權重在規範中有列舉,稍微看下,不難理解。其實這部操做也是最耗性能的,由於必需要作8*4次計算,並且每次計算要掃描整個數據陣列。其實前3種決策方法算起來還都好,最麻煩的是最後種,要計算m*n同色塊,每次出現須要加(m-1)*(n-1)*3,這個計算我沒有找到一個比較理想的算法,我變通的作法是,只計算出現機率最多的小塊矩形,2<=m<6,2<=n<6的共16種矩形,其實結果計算的差不了多少。其實不是說沒有算對就徹底掃不出來,這個選取操做可讓生成的二維碼最優化而已。這個操做在客戶端大概在百ms級別的,其實用戶是感覺不到它的生成過程,可是若是這個操做放在服務器端,可想而知壓力之大。