搞一个定制的二维码生成器
发表于:2024-03-14 |

前言

之前我很早就做过支付的内容了,但仔细玩这个二维码的配置项还是今天刚开始玩,今天记录一下这个二维码生成器如何来自定义,我这里就举几个例子,后续的操作就交给大家开发了。

前置准备

我们需要一个二维码的文字text,这个很简单,你随便找个二维码,然后去进行解码
这里给大家一个网址:https://cli.im/deqr
解码成功之后就得到了这个文本
二维码解码
或者你直接就用某个text,让人家扫出来是这段文字,这样也是可以的。

生成最简单的二维码

qrcode

环境与组件选择

这里我使用的库是qrcodejs2,这个在vue2的环境是可以使用的,在vue3的环境中,需要使用qrcodejs2-fix,这俩个的配置项是一样的,我就用vue3+qrcodejs2-fix来给大家大概展示下如何使用,react中我还没试过奥,大家可以自己试一下。

组件安装

1
npm i qrcodejs2-fix

基本二维码

我们用tdesign的弹窗组件来作为载体,然后写一个id为qrcode的div的dom来装载二维码,注意一下细节,在vue3中,我们必须使用nextTick保证qrcode存在,如果是vue2中二维码是在弹窗中的,最好也加上this.$nextTick。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<t-dialog v-model:visible="visible" :footer="null">
<t-row justify="center">
<div id="qrcode"></div>
</t-row>
</t-dialog>
</template>
<script setup lang="ts">
import {ref,onMounted,nextTick} from "vue";
import QRCode from "qrcodejs2-fix";

const visible= ref(true);

const codeText='wxp://f2f1bhovWje03yqtQ3mcg7QKDyMFyRP74tAccXHwVj5c-q_qoGwkWq79WfbzdWg9NFDd'
nextTick(()=>{
let qrcode = new QRCode("qrcode", {
text: codeText, // 二维码解码后的文本
width: 328, // 二维码宽度
height: 328, // 二维码长度
});
})
</script>

基础二维码

加个颜色

这里我加了颜色,将我的二维码变成了绿色

1
2
3
4
5
6
7
8
var qrcode = new QRCode("qrcode", {
text: codeText,
width: 328,
height: 328,
colorDark: '#008858', // 设置深色部分的颜色
colorLight: '#e3f9e9' // 设置浅色部分的颜色
correctLevel: QRCode.CorrectLevel.H, // 设置纠错级别
});

颜色二维码

没错,这个组件就结束了,那作为一个前端开发工程师,这个效果指定是不能满足咱们的需求的啊,花里胡哨才是咱们追求的,这时候我们完全可以自己写一个二维码封装的库,这里我就基于vue3写一个好了

根据qrcode完善功能

实现思路

我不知道大家看到这里有没有自己的思路了奥,其实最简单的就是我们调用一个生成二维码的组件,然后这个生成的二维码格式最好是图片的格式,之后我们将这个图片插入到canvas中,接下来的操作就简单了,我得思路也就是这样的。

修改dom

我就将他原来的dom作为一个虚假载体,直接display:none干掉,然后自己写一个canvas作为真实的载体显示。

1
2
3
4
5
6
7
8
<template>
<t-dialog v-model:visible="visible" :footer="null" :closeOnOverlayClick="false">
<t-row justify="center">
<div id="qrcode" style="display: none;"></div>
<canvas ref="qrcodeRef"></canvas>
</t-row>
</t-dialog>
</template>

添加图片和ref

我找了一张图片,然后定义了一个ref

1
2
const bgUrl=`https://rocket-chat.oss-cn-hangzhou.aliyuncs.com/202303/999999/imagesb7ab2054257e49c495e2540cd6434705.png`
const qrcodeRef=ref(null)

获取生成的二维码图片

1
2
3
4
5
6
7
8
9
const qrcode = new QRCode("qrcode", {
text: codeText,
width: 200,
height: 200,
colorDark: '#008858',
colorLight: '#e3f9e9',
correctLevel: QRCode.CorrectLevel.H
});
const elImg=qrcode._el.getElementsByTagName('img')[0]

等待图片加载完毕

1
2
3
4
const bgcImage=new Image()
bgcImage.setAttribute('crossOrigin', 'Anonymous')
bgcImage.src=bgUrl
bgcImage.onload=()=>{}

接下来的代码都是在onload里面的

设置canvas的宽高

这里我们就按照qrcode生成的图片的宽高来设置

1
2
qrcodeRef.value.width=200;
qrcodeRef.value.height=200;

获取上下文对象并清理画布

1
2
3
const ctx=qrcodeRef.value.getContext('2d')
// 清除画布
ctx.clearRect(0,0,200,200)

先画二维码

1
ctx.drawImage(elImg,0,0,200,200)

绘制背景图

需要添加透明度,这里我用了白色的0.5的透明度

1
2
ctx.fillStyle = "#fff";
ctx.globalAlpha = 0.5;

添加背景图

1
2
// 再画背景图
ctx.drawImage(bgcImage,0,0,200,200)

我们先将透明度改回来,这里我按照除以5来计算了

1
2
ctx.globalAlpha = 1;
ctx.drawImage(bgcImage,(200-200/5)/2,(200-200/5)/2,200/5,200/5)

这样一顿简单修改之后,就得到了我们的自定义二维码
自定义二维码

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<template>
<t-dialog v-model:visible="visible" :footer="null" :closeOnOverlayClick="false">
<t-row justify="center">
<div id="qrcode" style="display: none;"></div>
<canvas ref="qrcodeRef"></canvas>
</t-row>
</t-dialog>
</template>
<script setup lang="ts">
import {ref,onMounted,nextTick,reactive} from "vue";
import QRCode from "qrcodejs2-fix"

const visible= ref(true);

const codeText='wxp://f2f1bhovWje03yqtQ3mcg7QKDyMFyRP74tAccXHwVj5c-q_qoGwkWq79WfbzdWg9NFDd'

const bgUrl=`https://rocket-chat.oss-cn-hangzhou.aliyuncs.com/202303/999999/imagesb7ab2054257e49c495e2540cd6434705.png`
const qrcodeRef=ref(null)

onMounted(()=>{
nextTick(()=>{
const qrcode = new QRCode("qrcode", {
text: codeText,
width: 200,
height: 200,
colorDark: '#008858',
colorLight: '#e3f9e9',
correctLevel: QRCode.CorrectLevel.H
});
const elImg=qrcode._el.getElementsByTagName('img')[0]
const bgcImage=new Image()
bgcImage.setAttribute('crossOrigin', 'Anonymous')
bgcImage.src=bgUrl
bgcImage.onload=()=>{
qrcodeRef.value.width=200;
qrcodeRef.value.height=200;
const ctx=qrcodeRef.value.getContext('2d')
// 清除画布
ctx.clearRect(0,0,200,200)
// 先画二维码
ctx.drawImage(elImg,0,0,200,200)

// 必须设置透明度,不然背景看不见
ctx.fillStyle = "#fff";
ctx.globalAlpha = 0.5;

// 再画背景图
ctx.drawImage(bgcImage,0,0,200,200)

ctx.globalAlpha=1;

ctx.drawImage(bgcImage,(200-200/5)/2,(200-200/5)/2,200/5,200/5)
}

})
})
</script>

简单封装一下

组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<template>
<div>
<div id="qrcode" style="display: none;"></div>
<canvas ref="qrcodeRef"></canvas>
</div>
</template>
<script setup lang="ts">
import {ref,onMounted,nextTick,reactive,computed} from "vue";
import QRCode from "qrcodejs2-fix"
/*
@description 父组件传参
@params QRCodeOption 原有的QRCode参数,
如text:必传,
其他如width,height,colorDark, colorLight,correctLevel为选填
@params bgOption 背景传参,url是地址,opacity是不透明度,0是全透明,1是完全不透明,color:透明度的颜色
@params logoOption 中心logo传参,url是地址,opacity是不透明度,0是全透明,1是完全不透明,color:透明度的颜色
*/
const props=defineProps<{
QRCodeOption:Record<'text',string>&Record<string,any>,
bgOption?:{
url:string,
opacity?:number,
color?:string
},
logoOption?:{
url:string,
opacity?:number,
color?:string
},
}>()

const qrcodeRef=ref(null)

type TImgType='bg'|'logo'

interface IPromiseImg{
type:TImgType,
dom:HTMLImageElement
}

// 需要加载的图片数组
const imgArr=computed(()=>{
const arr:{
type:TImgType,
url:string
}[]=[];
const {bgOption,logoOption}=props
bgOption.url&&arr.push({
type:'bg',
url:bgOption.url
});
logoOption.url&&arr.push({
type:'logo',
url:logoOption.url
});
return arr
})

onMounted(()=>{
const {QRCodeOption,bgOption,logoOption}=props
nextTick(()=>{
// 生成二维码
const qrcode = new QRCode("qrcode", {
...QRCodeOption
});
// 获取二维码图片
const elImg=qrcode._el.getElementsByTagName('img')[0]
// 设置实际canvas二维码宽高
const {width=128,height=128}=QRCodeOption
qrcodeRef.value.width=width;
qrcodeRef.value.height=height;
// 获取上下文对象
const ctx=qrcodeRef.value.getContext('2d')
// 清除画布
ctx.clearRect(0,0,width,height);
// 图片加载的propmise
const promiseAll:Promise<IPromiseImg>[]=[]
// 设置需要加载的图片数组
imgArr.value.forEach((item)=>{
const promiseEach=new Promise<IPromiseImg>((resolve,reject)=>{
const image=new Image();
image.setAttribute('crossOrigin', 'Anonymous')
image.src=item.url;
image.onload=(()=>{
resolve({
dom:image,
type:item.type
})
})
image.onerror=(()=>{
reject('图片加载错误')
})
})
promiseAll.push(promiseEach)
})

// 所有图片加载完毕了
Promise.all(promiseAll).then((imgList)=>{
// 绘制二维码
ctx.drawImage(elImg,0,0,width,height)
// 是否有背景图
if(bgOption&&bgOption.url){
// 设置属性
ctx.fillStyle=bgOption.color??'#fff'
ctx.globalAlpha=bgOption.opacity??0.5
// 取值
const findItem=imgList.find(item=>item.type==='bg')
// 画背景图
ctx.drawImage(findItem.dom,0,0,width,height)
}
// 是否有logo
if(logoOption&&logoOption.url){
ctx.fillStyle=logoOption.color??'#fff'
ctx.globalAlpha=logoOption.opacity??1;
// 取值
const findItem=imgList.find(item=>item.type==='logo')
// 取中心值
const center=(width-width/5)/2
// 画背景图
ctx.drawImage(findItem.dom,center,center,width/5,height/5)
}
})
})
})

</script>

调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<template>
<t-dialog v-model:visible="visible" :footer="null" :closeOnOverlayClick="false">
<t-row justify="center">
<Qrcode :QRCodeOption="QRCodeOption" :bgOption="bgOption" :logoOption="logoOption" />
</t-row>
</t-dialog>
</template>
<script setup lang="ts">
import {ref} from "vue";
import Qrcode from "@/components/qrcode/index.vue"

const visible= ref(true);

const codeText='wxp://f2f1bhovWje03yqtQ3mcg7QKDyMFyRP74tAccXHwVj5c-q_qoGwkWq79WfbzdWg9NFDd'

const bgUrl=`https://rocket-chat.oss-cn-hangzhou.aliyuncs.com/202303/999999/imagesb7ab2054257e49c495e2540cd6434705.png`

const QRCodeOption={
text: codeText,
width: 200,
height: 200,
colorDark: '#008858',
colorLight: '#e3f9e9',
}

const bgOption={
url:bgUrl
}

const logoOption={
url:bgUrl
}

</script>

vue3中封装qrcode2-fix就是这样,如果是vue2中封装qrcode2,原理是一样的

结语

本篇文章就到这里了,更多内容敬请期待~

上一篇:
【JAVA学习】05-跟着黑马程序员课程敲全栈项目(二)
下一篇:
try-catch和Promise.then一起使用的bug