本文正在参加「 . 」
写在开头
最近在项目中遇到一个紧急的需求,大概要求就是前端接收后端接口信息后,需要独自生成带文字的二维码图片,并能批量打包成 .zip
包,提供下载。
为了在最快时间内完成需求,小编直接采用网上比较成熟的方案来做,具体过程如下:
qrcode
:用于生成二维码。html2canvas
:用于绘制二维码与文字,并生成图片。jszip
:用于把文件生成.zip
包。file-saver
:用于下载文件。
以上是我们需要使用到的 npm
包,可以先来把它们安装到项目中:
npm install qrcode html2canvas jszip file-saver -S
用qrcode生成二维码
qrcode 包是一个用于生成二维码的 javascript
库,它的使用也是非常简单,下面我们来具体看看如何生成一个二维码。
考虑到后面会生成多个二维码,所以把二维码生成单独抽离成一个组件,新建 Qrcode.vue
文件:
<template>
<div class="qr">
<img class="qr__img" :src="qrBase64" />
</div>
</template>
<script>
import QRcode from 'qrcode'
export default {
props: {
qrContent: {
type: String,
default: ''
}
},
data () {
return {
qrBase64: ''
}
},
watch: {
qrContent: {
handler: function () {
this.qrContent && this.init()
},
immediate: true
}
},
methods: {
async init () {
this.qrBase64 = await this.createQR(this.qrContent)
},
// 生成二维码
createQR (content, options) {
return new Promise((resolve, reject) => {
QRcode.toDataURL(content, options).then(url => {
resolve(url)
})
})
},
}
}
</script>
<style scoped>
.qr{
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.qr__img{
width: 100%;
height: 200px;
}
</style>
在 App.vue
文件中引入使用:
<template>
<div class="box">
<div v-for="(item, index) in qrData" :key="index" class="box-item">
<Qrcode :qrContent="item.content" />
</div>
</div>
</template>
<script>
import Qrcode from './components/Qrcode.vue';
export default {
components: { Qrcode },
data() {
return {
qrData: [
{ content: 'https://Admin.net' },
{ content: 'https://Admin.net/user/1908407919184670'},
{ content: 'https://www.baidu.com' },
]
}
},
}
</script>
<style scoped>
.box{
margin: 20px;
display: flex;
flex-wrap: wrap;
}
.box-item{
width: 200px;
height: fit-content;
border: 1px solid #eee;
margin-right: 12px;
margin-bottom: 12px;
}
</style>
很简单我们就完成了第一步了。
用html2canvas生成二维码与文字图片
第二步我们来给二维码添加一些文案描述,并让二维码和文案结合生成一张图片,这样才方便使用人员的传播。
html2canvas 包能快速帮我们完成这件事情,它的原理是利用了 canvas
技术来绘制图片与文字,再统一转成图片输出,具体过程我们接着往下来看。
App.vue
文件:
<template>
<div class="box">
<div v-for="(item, index) in qrData" :key="index" class="box-item">
<Qrcode :qrContent="item.content">
<div v-if="item.type == 1" class="text">
<div class="black">{{ item.date }}</div>
<div class="black">{{ item.time }}</div>
</div>
<div v-if="item.type == 2" class="text">
<div class="sub-title">课程内容</div>
<div class="desc">{{ item.desc }}</div>
<div class="sub-title">起止时间</div>
<div class="black">{{ item.time }}</div>
</div>
</Qrcode>
</div>
</div>
</template>
<script>
import Qrcode from './components/Qrcode.vue';
export default {
components: { Qrcode },
data() {
return {
qrData: [
{
content: 'https://Admin.net',
type: 1,
date: '2022年11月08号',
time: '上午'
},
{
content: 'https://Admin.net',
type: 1,
date: '2022年11月08号',
time: '下午'
},
{
content: 'https://Admin.net/user/1908407919184670',
type: 2,
time: '11/08 09:30 - 10:30',
desc: '关于Vue3的知识点内容的学习,带你征服Vue3',
},
{
content: 'https://www.baidu.com',
type: 2,
time: '11/08 02:30 - 16:30',
desc: '学习React内容的课程',
},
]
}
},
}
</script>
<style scoped>
...
.text{
text-align: center;
box-sizing: border-box;
padding: 2px 30px 14px 30px;
font-size: 12px;
color: #333;
}
.text div{
margin: 4px 0;
}
.black{
color: #000;
font-weight: bold;
}
.desc{
margin-bottom: 16px;
}
.sub-title{
color: #999;
}
</style>
为了方便不同文案描述的布局,我们以插槽 slot
的形式来插入文案描述。
Qrcode.vue
文件:
<template>
<div class="qr" :id="uuid">
<img class="qr__img" :src="qrBase64" @load="imgLoad" />
<slot />
</div>
</template>
<script>
import QRcode from 'qrcode'
import html2canvas from 'html2canvas'
export default {
props: {
qrContent: {
type: String,
default: ''
}
},
data () {
return {
uuid: `qr_${this._uid}_code`, // 组件唯一ID
qrBase64: ''
}
},
watch: {
qrContent: {
handler: function () {
this.qrContent && this.init()
},
immediate: true
}
},
methods: {
async init () {
this.qrBase64 = await this.createQR(this.qrContent)
},
createQR (content, options) {
return new Promise((resolve, reject) => {
QRcode.toDataURL(content, options).then(url => {
resolve(url)
})
})
},
createCanvas () {
return new Promise((resolve, reject) => {
const container = document.querySelector(`#${this.uuid}`)
html2canvas(container).then(canvans => {
const base64 = canvans.toDataURL('image/png')
resolve(base64)
})
})
},
// 需要等图片加载完才能生成canvas
async imgLoad () {
const canvansBase64 = await this.createCanvas()
console.log(canvansBase64) // 生成二维码+文案图片
}
}
}
</script>
这里有个注意点是需要等图片加载完,我们才能去初始化 html2canvas
,否则二维码可能会是空白一片,可以监听 <img />
标签的 load
事件来判断这个时机。
用jszip生成压缩包
完成第二步后,我们就获取到每个图片的 base64
了。下面我们把这些信息收集起来,然后丢给 jszip 包就大功告成了。
这里我们采用 v-model
的形式来收集。
Qrcode.vue
文件:
...
export default {
props: {
...
value: {
type: String,
default: ''
},
},
methods: {
...,
async imgLoad () {
const canvansBase64 = await this.createCanvas()
this.$emit('input', canvansBase64) // 把目标base64丢出去
}
}
...
}
App.vue
文件:
<template>
<div class="box">
<div v-for="(item, index) in qrData" :key="index" class="box-item">
<Qrcode :qrContent="item.content" v-model="item.url">
...
</Qrcode>
</div>
</div>
</template>
<script>
import Qrcode from './components/Qrcode.vue';
import JsZip from 'jszip'
export default {
components: { Qrcode },
data() {
return {
qrData: [
{
...,
url: ''
},
{
...,
url: ''
},
{
...,
url: ''
},
{
...,
url: ''
},
]
}
},
methods: {
pack() {
const zip = new JsZip()
const folder = zip.folder('创建文件夹')
this.qrData.forEach((item, index) => {
// 不需要base64的头部
zip.file(`${index}_${item.type}.png`, item.url.split(',')[1], { base64: true })
})
zip.generateAsync({ type: 'blob' }).then(content => {
console.log(content); // 压缩包
})
}
}
}
</script>
用file-saver完成下载
最后,我们使用 file-saver
来将压缩包下载下来。
App.vue
文件:
<template>
<div>
<button @click="download">一键下载所有码</button>
<div class="box">
...
</div>
</div>
</template>
<script>
import Qrcode from './components/Qrcode.vue';
import JsZip from 'jszip'
import { saveAs } from 'file-saver'
export default {
methods: {
download () {
const QRRes = this.qrData.some(item => item.url === '')
if (QRRes) {
console.warn('当前存在一些二维码未生成完毕,这些二维码将无法下载')
}
this.pack()
},
pack() {
const zip = new JsZip()
const folder = zip.folder('创建文件夹')
this.qrData.forEach((item, index) => {
zip.file(`${index}_${item.type}.png`, item.url.split(',')[1], { base64: true })
})
zip.generateAsync({ type: 'blob' }).then(content => {
saveAs(content, '我是新鲜出炉的压缩包唷')
})
}
}
}
可以看到效果整体还是不错的,开开心心收工。
至此,本篇文章就写完啦,撒花撒花。
希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。
声明:本文仅供个人学习使用,来源于互联网,本文有改动,本文遵循[BY-NC-SA]协议, 如有侵犯您的权益,请联系本站,本站将在第一时间删除。谢谢你