Eswlnk Blog Eswlnk Blog
  • 资源
    • 精彩视频
    • 破解专区
      • WHMCS
      • WordPress主题
      • WordPress插件
    • 其他分享
    • 极惠VPS
    • PDF资源
  • 关于我
    • 论文阅读
    • 关于本站
    • 通知
    • 左邻右舍
    • 玩物志趣
    • 日志
    • 专题
  • 热议话题
    • 游戏资讯
  • 红黑
    • 渗透分析
    • 攻防对抗
    • 代码发布
  • 自主研发
    • 知识库
    • 插件
      • ToolBox
      • HotSpot AI 热点创作
    • 区块
    • 快乐屋
    • 卡密
  • 乱步
    • 文章榜单
    • 热门标签
  • 问答中心反馈
  • 注册
  • 登录
首页 › 代码发布 › 解读二维码美化的实现原理

解读二维码美化的实现原理

Eswlnk的头像
Eswlnk
2022-04-28 22:38:05
解读二维码美化的实现原理-Eswlnk Blog
智能摘要 AI
本文介绍了如何制作和美化艺术二维码,强调其在现代生活中的重要性。文章首先讲解了二维码定位点的绘制规则,指出定位点占据二维码的特定区域,其大小不受纠错级别的影响。接着阐述了信息点的绘制,通过二维数组表示二维码结构,并根据需求插入自定义素材。随后讨论了背景图的添加,确保背景图在二维码之前绘制,增加视觉效果。最后提供了完整的代码示例及参数说明,帮助用户生成个性化艺术二维码。代码和参数文档可在GitHub上获取。

二维码在现代生活中已经处在了不可替换的地位,使用二维码进行收款候着分享现在也是一种呈上升趋势的潮流。本篇文章将会带给喜爱二维码制作爱好者一份别样的惊喜,带领您们了解二维码制作和美化的底层原理,且听我娓娓道来。

解读二维码美化的实现原理-Eswlnk Blog

1、定位点绘制

完成艺术二维码的焦点是怎样将素材按指定请求衬着到布景图上去。二维码的详细生产规律还没有明白,不过从通过小法式二维码生产库weapp-qrcode生产的二维码中能够发掘,若将二维码举行平均划分,左上角、右上角和左下角的三个定位点宽高都占有了七份,这个值是在经由点窜二维码纠错级别 correctLevel后不管怎样也不会产生转变的。

这样,咱们便可计较出三个定位点的宽高和地位:以二维码的宽(width)、高(height)、横向点阵数目(num)为例,辣么单个点阵占有的宽高为 pWidth = width / num,定位点的宽高为 (width / num) * 7,左上角定位点的肇始绘制地位为 (0, 0),右上角的肇始绘制地位为 ((num - 1) - 7) * pWidth, 0),左下角的肇始绘制地位为 (0, (num - 1) - 7) * pWidth)。

2、信息点绘制

二维码的信息点可以根据自己的需要定制,一般来说占用面积越小,出现的概率越大。这里我预设了如下图所示可以使用的信息点:

解读二维码美化的实现原理-Eswlnk Blog

我们可以将二维码转换为一个二维数组,有数据的位置标志为1,没有数据的位置标记为0,那么一个二维码可以表示为:

[
    [1, 1, 1, 1, 1, 1, 0, 0, 1, 0, ....],
    [0, 1, 0, 1, 1, 1, 0, 0, 1, 0, ....],
    [0, 1, 0, 1, 1, 1, 0, 0, 1, 0, ....],
    [0, 1, 0, 1, 1, 1, 0, 0, 1, 0, ....],
    [0, 1, 0, 1, 1, 1, 0, 0, 1, 0, ....],
    [0, 1, 0, 1, 1, 1, 0, 0, 1, 0, ....],
    [0, 1, 0, 1, 1, 1, 0, 0, 1, 0, ....],
    [0, 1, 0, 1, 1, 1, 0, 0, 1, 0, ....],
    [0, 1, 0, 1, 1, 1, 0, 0, 1, 0, ....],
    [0, 1, 0, 1, 1, 1, 0, 0, 1, 0, ....],
    [0, 1, 0, 1, 1, 1, 0, 0, 1, 0, ....],
    ...
]

除去三个定位点,我们可以通过遍历是否存在可以放置指定比例素材的位置,如果存在,将素材和素材的位置进行记录。我们以row2col2为例,设第一个信息点的位置为 (x, y),这时去循环判断 (x, y)、(x + 1, y)、(x, y + 1)、(x + 1, y + 1)这四个位置的标志是否都为1,如果满足条件,则将这四个位置标记为已使用,并将起始位置 (x, y)添加到row2col2需要渲染的位置中。

将所有的能够渲染的素材遍历完成之后,我们会得到素材对应的坐标位置,然后将素材进行渲染即可。下面为实现艺术二维码生成的部分代码:

function drawQrcode(options) {
    options = options || {};
    options = extend(true, {
        width: 256,
        height: 256,
        x: 0,
        y: 0,
        typeNumber: -1,
        correctLevel: QRErrorCorrectLevel.H,
        background: '#ffffff',
        foreground: '#000000',
        image: {
            imageResource: '',
            dx: 0,
            dy: 0,
            dWidth: 256,
            dHeight: 256
        },
        materials: {
            eye: [],
            col4: [],
            row4: [],
            row2col3: [],
            row3col2: [],
            col3: [],
            row3: [],
            row2col2: [],
            col2: [],
            row2: [],
            single: []
        }
    }, options);

    if (!options.canvasId && !options.ctx) {
        console.warn('please set canvasId or ctx!');
        return;
    }

    createCanvas();

    function createCanvas() {

        // create the qrcode itself
        var qrcode = new QRCode(options.typeNumber, options.correctLevel);
        qrcode.addData(utf16to8(options.text));
        qrcode.make();

        // get canvas context
        var ctx;
        if (options.ctx) {
            ctx = options.ctx;
        } else {
            ctx = options._this ? wx.createCanvasContext && wx.createCanvasContext(options.canvasId, options._this) : wx.createCanvasContext && wx.createCanvasContext(options.canvasId);
        }

        // count dots
        let dotArray = []
        for (var row = 0; row < qrcode.getModuleCount(); row++) {
            let arr = []
            for (var col = 0; col < qrcode.getModuleCount(); col++) {
                arr.push(qrcode.isDark(row, col) ? 1 : 0)
            }
            dotArray.push(arr)
        }

        let descPosition = {
            eye: [],
            col4: [],
            row4: [],
            row2col3: [],
            row3col2: [],
            col3: [],
            row3: [],
            row2col2: [],
            col2: [],
            row2: [],
            single: []
        }


        let copyDotArray = dotArray.map(item => item.map(iitem => false))

        function isMatchRule(rowIndex, colIndex) {
            return copyDotArray[rowIndex] && copyDotArray[rowIndex][colIndex] === false && dotArray[rowIndex][colIndex] === 1
        }

        // position dot
        dotArray.forEach((row, rowIndex) => {
            row.forEach((col, colIndex) => {
                if ((rowIndex < 7 && colIndex < 7) || (rowIndex < 7 && colIndex > row.length - 1 - 7) || (rowIndex > row.length - 1 - 7 && colIndex < 7)) {
                    copyDotArray[rowIndex][colIndex] = true
                    if ((rowIndex === 0 && colIndex === 0) || (rowIndex === 0 && colIndex === row.length - 7) || (rowIndex === row.length - 7 && colIndex === 0)) {
                        descPosition.eye.push([rowIndex, colIndex])
                    }
                }
            })
        })

        // not position dot
        dotArray.forEach((row, rowIndex) => {
            row.forEach((col, colIndex) => {
                if ((rowIndex < 7 && colIndex < 7) || (rowIndex < 7 && colIndex > row.length - 1 - 7) || (rowIndex > row.length - 1 - 7 && colIndex < 7)) {

                } else {
                    // col4
                    if (options.materials.col4.length && isMatchRule(rowIndex, colIndex) && isMatchRule(rowIndex, colIndex + 1) && isMatchRule(rowIndex, colIndex + 2) && isMatchRule(rowIndex, colIndex + 3)) {
                        copyDotArray[rowIndex][colIndex] = copyDotArray[rowIndex][colIndex + 1] = copyDotArray[rowIndex][colIndex + 2] = copyDotArray[rowIndex][colIndex + 3] = true
                        descPosition.col4.push([rowIndex, colIndex])
                    }

                    // row4
                    if (options.materials.row4.length && isMatchRule(rowIndex, colIndex) && isMatchRule(rowIndex + 1, colIndex) && isMatchRule(rowIndex + 2, colIndex) && isMatchRule(rowIndex + 3, colIndex)) {
                        copyDotArray[rowIndex][colIndex] = copyDotArray[rowIndex + 1][colIndex] = copyDotArray[rowIndex + 2][colIndex] = copyDotArray[rowIndex + 3][colIndex] = true
                        descPosition.row4.push([rowIndex, colIndex])
                    }

                    // row2col3
                    if (options.materials.row2col3.length && isMatchRule(rowIndex, colIndex) && isMatchRule(rowIndex, colIndex + 1) && isMatchRule(rowIndex, colIndex + 2) && isMatchRule(rowIndex + 1, colIndex) && isMatchRule(rowIndex + 1, colIndex + 1) && isMatchRule(rowIndex + 1, colIndex + 2)) {
                        copyDotArray[rowIndex][colIndex] = copyDotArray[rowIndex][colIndex + 1] = copyDotArray[rowIndex][colIndex + 2] = copyDotArray[rowIndex + 1][colIndex] = copyDotArray[rowIndex + 1][colIndex + 1] = copyDotArray[rowIndex + 1][colIndex + 2] = true
                        descPosition.row2col3.push([rowIndex, colIndex])
                    }

                    // row3col2
                    if (options.materials.row3col2.length && isMatchRule(rowIndex, colIndex) && isMatchRule(rowIndex, colIndex + 1) && isMatchRule(rowIndex + 1, colIndex) && isMatchRule(rowIndex + 1, colIndex + 1) && isMatchRule(rowIndex + 2, colIndex) && isMatchRule(rowIndex + 2, colIndex + 1)) {
                        copyDotArray[rowIndex][colIndex] = copyDotArray[rowIndex][colIndex + 1] = copyDotArray[rowIndex + 1][colIndex] = copyDotArray[rowIndex + 1][colIndex + 1] = copyDotArray[rowIndex + 2][colIndex] = copyDotArray[rowIndex + 2][colIndex + 1] = true
                        descPosition.row3col2.push([rowIndex, colIndex])
                    }

                    // col3
                    if (options.materials.col3.length && isMatchRule(rowIndex, colIndex) && isMatchRule(rowIndex, colIndex + 1) && isMatchRule(rowIndex, colIndex + 2)) {
                        copyDotArray[rowIndex][colIndex] = copyDotArray[rowIndex][colIndex + 1] = copyDotArray[rowIndex][colIndex + 2] = true
                        descPosition.col3.push([rowIndex, colIndex])
                    }

                    // row3
                    if (options.materials.row3.length && isMatchRule(rowIndex, colIndex) && isMatchRule(rowIndex + 1, colIndex) && isMatchRule(rowIndex + 2, colIndex)) {
                        copyDotArray[rowIndex][colIndex] = copyDotArray[rowIndex + 1][colIndex] = copyDotArray[rowIndex + 2][colIndex] = true
                        descPosition.row3.push([rowIndex, colIndex])
                    }

                    // row2col2
                    if (options.materials.row2col2.length && isMatchRule(rowIndex, colIndex) && isMatchRule(rowIndex, colIndex + 1) && isMatchRule(rowIndex + 1, colIndex) && isMatchRule(rowIndex + 1, colIndex + 1)) {
                        copyDotArray[rowIndex][colIndex] = copyDotArray[rowIndex][colIndex + 1] = copyDotArray[rowIndex + 1][colIndex] = copyDotArray[rowIndex + 1][colIndex + 1] = true
                        descPosition.row2col2.push([rowIndex, colIndex])
                    }

                    // col2
                    if (options.materials.col2.length && isMatchRule(rowIndex, colIndex) && isMatchRule(rowIndex, colIndex + 1)) {
                        copyDotArray[rowIndex][colIndex] = copyDotArray[rowIndex][colIndex + 1] = true
                        descPosition.col2.push([rowIndex, colIndex])
                    }

                    // row2
                    if (options.materials.row2.length && isMatchRule(rowIndex, colIndex) && isMatchRule(rowIndex + 1, colIndex)) {
                        copyDotArray[rowIndex][colIndex] = copyDotArray[rowIndex + 1][colIndex] = true
                        descPosition.row2.push([rowIndex, colIndex])
                    }

                    // single
                    if (options.materials.single.length && isMatchRule(rowIndex, colIndex)) {
                        copyDotArray[rowIndex][colIndex] = true
                        descPosition.single.push([rowIndex, colIndex])
                    }
                }
            })
        })

        if (options.image.imageResource) {
            ctx.drawImage(options.image.imageResource, options.image.dx, options.image.dy, options.image.dWidth, options.image.dHeight);
        }

        // compute tileW/tileH based on options.width/options.height
        var tileW = options.width / qrcode.getModuleCount();
        var tileH = options.height / qrcode.getModuleCount();

        // draw materials
        function drawMaterials(type, colNum, rowNum) {
            descPosition[type].forEach((item, index) => {
                ctx.drawImage(options.materials[type][Math.floor((Math.random() * options.materials[type].length))], options.x + item[1] * tileW, options.y + item[0] * tileH, tileW * colNum, tileH * rowNum)
            })
        }

        drawMaterials('eye', 7, 7)
        drawMaterials('col4', 4, 1)
        drawMaterials('row4', 1, 4)
        drawMaterials('row2col3', 2, 3)
        drawMaterials('row3col2', 3, 2)
        drawMaterials('col3', 3, 1)
        drawMaterials('row3', 1, 3)
        drawMaterials('row2col2', 2, 2)
        drawMaterials('col2', 2, 1)
        drawMaterials('row2', 1, 2)
        drawMaterials('single', 1, 1)

        // callback
        ctx.draw(false, function(e) {
            options.callback && options.callback(e);
        });
    }
}

3、添加背景图

只绘制一个艺术二维码有时候会比较单调,这时可以为艺术二维码添加背景图,然后将二维码按一定比例在背景图上。需要注意的是, 背景图的绘制需要在二维码之前。

4、使用方法

最终的源码我放在了github:

https://github.com/BWmelon/artQrcode

在页面中添加一个 canvas标签和 image标签,代码如下:

<image src="{{url}}" class="image" mode="widthFix" bindtap="handlePreview" style="width: 750rpx;"></image>
<canvas style="width: 900px; height: 1200px;position: absolute;left: -99999rpx;" canvas-id="myQrcodeOne"></canvas>

在js中引入生成库:

import drawQrcode from '../../utils/weapp.artQrcode.js'

生成艺术二维码

drawQrcode({
    width: 520,
    height: 520,
    canvasId: 'myQrcodeOne',
    // ctx: wx.createCanvasContext('myQrcodeOne'),
    text: 'https://qr.no0a.cn',
    x: 180,
    y: 100, // v1.0.0+版本支持在二维码上绘制图片
    image: {
        imageResource: '../../images/materials/xiaohuangren/border.png',
        dx: 0,
        dy: 0,
        dWidth: 900,
        dHeight: 1200
    },
    materials: {
        eye: ["../../images/materials/xiaohuangren/eye.png"],
        row3: ["../../images/materials/xiaohuangren/row3.png"],
        row2col3: ["../../images/materials/xiaohuangren/row2col3.png"],
        row2col2: ["../../images/materials/xiaohuangren/row2col2.png", "../../images/materials/xiaohuangren/row2col2_2.png"],
        row2: ["../../images/materials/xiaohuangren/row2.png", "../../images/materials/xiaohuangren/row2_2.png"],
        single: ["../../images/materials/xiaohuangren/single.png"],
    },
    callback: () => {
        wx.canvasToTempFilePath({
            canvasId: 'myQrcodeOne',
            success: (res) => {
                console.log(res.tempFilePath)
                this.setData({
                    url: res.tempFilePath
                })
            }
        })
    }
})

5、参数说明

参数说明类型示例
width必须,二维码宽度Number520
height必须,二维码高度Number520
text必须,二维码内容Stringhttps://github.com/BWmelon/artQrcode
canvasId必须,绘制的canvasIdString'myQrcode'
x非必须,二维码x轴相对于背景图起始位置,默认为0Number100
y非必须,二维码y轴相对于背景图起始位置,默认为0Number100
correctLevel非必须,二维码纠错级别,默认值为高级,取值:{ L: 1, M: 0, Q: 3, H: 2 }Number1
_this非必须,若在组件中使用,需要传入this
callback非必须,绘制完成后的回调函数Function() => {console.log(‘完成’)}
image必须,背景图 image.imageResource:背景图位置 image.dx:背景图起始x轴 image.dy:背景图起始y轴 image.dWidth:背景图绘制宽度 image.dHeight:背景图绘制高度Object{ imageRosourse: ‘../../images/materials/border.png’, dx: 0, dy: 0, dWidth: 900, dHeight: 1200 }
materials必须,素材 可选值:eye、col4、row4、row2col3、row3col2、col3、row3、row2col2、col2、row2、single 每一项的值都是一个数组,如果该项素材数量大于1,将会进行随机渲染 没有素材的选项可以不填或者为空数组Object{ eye: [“../../images/materials/xiaohuangren/eye.png”], row3:[“../../images/materials/xiaohuangren/row3.png”], row2col3: [“../../images/materials/xiaohuangren/row2col3.png”], row2col2: [“../../images/materials/xiaohuangren/row2col2.png”, “../../images/materials/xiaohuangren/row2col2_2.png”], row2: [“../../images/materials/xiaohuangren/row2.png”, “../../images/materials/xiaohuangren/row2_2.png”], single: [“../../images/materials/xiaohuangren/single.png”] }
本站默认网盘访问密码:1166
本站默认网盘访问密码:1166
二维码二维码绘制美化二维码
0
0
Eswlnk的头像
Eswlnk
一个有点倒霉的研究牲站长
赞赏
API接口上云|Serverless云函数服务部署实战
上一篇
刷新百度小程序云缓存
下一篇

评论 (0)

请登录以参与评论
现在登录
    发表评论

猜你喜欢

  • 「日志记录」逆向必应翻译网页版API实现免费调用
  • 「代码分享」第三方平台VIP视频解析API接口
  • 「至臻原创」某系统网站登录功能监测
  • 「开发日志」在Vue3中如何为路由Query参数标注类型
  • 「其他分享」分享一个在Tun模式下可用的脚本
Eswlnk的头像

Eswlnk

一个有点倒霉的研究牲站长
1108
文章
319
评论
679
获赞

随便看看

「代码分享」移除WordPress头部自带的全局内联CSS
2022-08-14 16:09:56
「教程分享」如何解决 WordPress 添加媒体按钮突然失效的问题
2022-10-23 20:46:17
「原创首发」WordPress弹幕插件Barrager修复与更新发布
2022-11-06 0:30:25

文章目录

专题展示

WordPress53

工程实践37

热门标签

360 AI API CDN java linux Nginx PDF PHP python SEO Windows WordPress 云服务器 云服务器知识 代码 免费 安全 安卓 工具 开发日志 微信 微软 手机 插件 攻防 攻防对抗 教程 日志 渗透分析 源码 漏洞 电脑 破解 系统 编程 网站优化 网络 网络安全 脚本 苹果 谷歌 软件 运维 逆向
  • 首页
  • 知识库
  • 地图
Copyright © 2023-2025 Eswlnk Blog. Designed by XiaoWu.
本站CDN由 壹盾安全 提供高防CDN安全防护服务
蜀ICP备20002650号-10
页面生成用时 0.473 秒   |  SQL查询 49 次
本站勉强运行:
友情链接: Eswlnk Blog 网站渗透 倦意博客 特资啦!个人资源分享站 祭夜博客 iBAAO壹宝头条
  • WordPress142
  • 网络安全64
  • 漏洞52
  • 软件52
  • 安全48
现在登录
  • 资源
    • 精彩视频
    • 破解专区
      • WHMCS
      • WordPress主题
      • WordPress插件
    • 其他分享
    • 极惠VPS
    • PDF资源
  • 关于我
    • 论文阅读
    • 关于本站
    • 通知
    • 左邻右舍
    • 玩物志趣
    • 日志
    • 专题
  • 热议话题
    • 游戏资讯
  • 红黑
    • 渗透分析
    • 攻防对抗
    • 代码发布
  • 自主研发
    • 知识库
    • 插件
      • ToolBox
      • HotSpot AI 热点创作
    • 区块
    • 快乐屋
    • 卡密
  • 乱步
    • 文章榜单
    • 热门标签
  • 问答中心反馈