背景
因项目开发经常会需要使用到二维码,无论是使用React或Vue框架都会找一找相关的封装好的框架,看着都是基于qr.js或者qrcode.js套一个外壳,还有些其他的版本的实现,看了看和qr.js
大同小异,甚至可以说都是直接基于qr.js
添加些功能。看着这个项目都是8年前的项目了,说明二维码的生成算法稳定的同时也是反应出其比较复杂,以至于都没有人重新造轮子?
由于经常用到二维码,不了解了解原理,总感觉缺失点乐趣。尝试找过些文章:
- 二维码的生成细节和原理
- 深度探索二维码原理及其应用
- 二维码生成原理及解析代码
- qrcode tutorial
感觉都是照着规范再介绍,博客文章内容也差不多,反正看完不仅没有看懂,还被劝退了几次。本着不能认怂的态度,这周决定还是从看源码 + 看博客 => 手撸简易版二维码实现
。这个过程整整花了 1 天左右,撸完代码后,对二维码的一些概念还是不太理解,尤其是错误码相关的,涉及应该很复杂的数学知识,纠结甚久,还是放弃。。
文章涉及代码可以看 简易版版二维码实现
实战准备
选择源码
用过好几个版本的二维码,例如qrcode-terminal
,qrcode.react
,qrcode.js
等,从github上看其对应的qrcode实现,qrcode-terminal
里面包含qrcode实现; qrcode.react
引用了qr.js
; qrcode.js
也直接就包含qrcode的实现,但是压缩版本。对比后,选择了阅读qr.js
,选择它的原因,看起来更像是是原创的,而且结构也清晰些。
理论准备
实战之前,还是推荐看 二维码生成原理及解析代码,直接上手源码会迷失在代码细节中,对照着这个文档先简单看看源码。至少这个图得先了解清楚:
环境准备
手撸代码前,因为是本地开发,为了方便预览、调试,期望本地可以直接在 terminal 中展示二维码,故直接先"借鉴" qrcode-terminal
的做法,先完成了一个terminal预览二维码的工具,具体可以参考 Terminal展示二维码
手撸代码
手撸代码实现了一个生成 hello world
的二维码,先上二维码:
代码结构
先大致看了一遍源码后,结合文档二维码生成原理及解析代码中生成二维码过程,思路就根据如下图片进行: 因为设想是完成一个简单版本的二维码生成器,为了方便,所有代码都放在一个文件中,代码结构为:
具体代码实现可以看简易版版二维码实现
1.初始化
因为二维码有较多版本,不同版本的尺寸不一样,本想实现 version1 版本,但过于简单,连个对齐图案都没有,故就实现了 version2版本,使用的字节编码(Byte Mode)模式,不同模式生成二维码对应的二进制位数不一样。在初始化时就设定好版本,同时创建一个modules属性存储二维码图案信息,使用 1 标识黑块,0 标识白块,null标识还未被处理,吐血的经验,图案信息需要三个状态
,一开始实现我都初始化为 0,后面处理数据码和纠错码时,死活会有问题。
2.定位图案的实现
定位图案是分别在左上、右上、左下三个角的方块,有固定的尺寸大小,如下图:
在实现时,千万不能忘记了定位图片需要被白色块包裹,也就是理论准备中二维图中的分割器(Separators fro Position Detection Patterns),此处又踩了个坑。实现方式也相对源码取巧,生成一个二维数组标识方块,如下图,然后遍历该二维数组,设置对应modules属性中位置:
同理,对齐图案因为也是一个固定大小的方块,实现方式也类似,就不赘述了,具体可以看源码。 效果图为:
3. 时序图案实现
时序图案就是定位图片三个角连成的两条线,应该是里面最简单的一个:
对应效果为:
4. 版本&格式信息
版本和格式信息一共 15 bits,包含 5bits 的数据位 + 10bits 纠错位。其中数据位为 2bits 纠错等级 + 3bits蒙版。一共会有两个位置会存储,如下图的左边那个。涉及到纠错位的生成,开始需要用到些数学知识,我也不太懂啊,照着 qr.js
中的实现改造了些,代码就直接看源码吧,上个效果图:
5. 数据码&纠错码
最难的地方了,看了最长的时间,里面涉及到二进制的操作,和常规运算不太一样。主要难点有两个:
- 头部为4bit的编码模式,其次是8bit的数据大小,排列后还需要根据8bit生成数据码,源码不好理解,用常规思路改造了一下。
- 纠错码会涉及到多项式的计算,恕我太菜,这块真的懵逼了,直接复制
qr.js
源码的中实现。
使用下述函数生成对应位数的二进制编码
function getBinary(num, length) {
const long = '00000000000000000000000000000000' // 长度没啥具体含义,保证比 length大就好
const binary = num.toString(2)
if (binary.length < length) {
return long.slice(0, length - binary.length) + binary
}
return binary
}
具体使用如下: 后面的纠错码也是懵逼状态,涉及到多项式,蒙版图案,可能是文案太简单,试了试不同蒙版(对应源码中的maskPattern字段,可0-7共8中模式),也都差不多,直接上效果图吧:
总结
手撸二维码实现,虽然实际没有多大意义,但心理上还是有些喜悦,破除一个魔障。。虽然涉及到较多理论知识,但看不懂的就过,能用自己方式实现的就用自己的方式实现,无所谓好与坏,用自己方式实现,拓宽自己的思路,同时也是加深理解的一种方式。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!