- 来源需求
项目宣传海报,海报上有项目入口二维码,由于该海报的二维码是实时生成,因此不能用固定的二维码,所以才需要调用后端接口获取到对应的链接,然后转成二维码,最后拼接到海报图上,再进行展示。 - 功能实现
1、由于项目使用的是vue3框架,所以采用qrcode插件将链接转成二维码。文档地址
2、拼接方式则是通过canvas将上一步生成好的二维码按照指定位置绘至海报图上。canvas文档
3、通过json文件配置海报图和二维码位置。
4、支持区分海报类型。(需要在代码中约定好类型枚举值)
5、支持生成多张,使用van-swipe进行滑动切换海报。 - 具体代码
<div class="banner" id="banner">
<van-swipe @change="swipeChange" :loop="false" :initial-swipe="bannerIndex">
<van-swipe-item v-for="(img, index) in posterImage" :key="index">
<img :src="img" />
</van-swipe-item>
</van-swipe>
</div>
const route = useRoute()
const productType = route.query.posterType|| ''
const bannerIndex = ref(0)
const posterImage = ref([])
const swipe = ref(null)
const swipeChange = index => {
bannerIndex.value = index
}
const canvasPoster = (imgList, code) => {
const images = []
const imagesOnload = []
imgList.forEach((img, index) => {
images[index] = new Image()
images[index].setAttribute('crossOrigin', 'anonymous')
images[index].src = img.url
images[index].position = img.position
imagesOnload[index] = new Promise((resolve, reject) => {
images[index].onload = () => resolve()
images[index].onerror = () => reject()
})
})
const invitationImg = new Image()
invitationImg.src = code
const invitationOnload = new Promise((resolve, reject) => {
invitationImg.onload = () => resolve()
})
const resList = []
return Promise.all([...imagesOnload, invitationOnload]).then(
res => {
images.forEach(img => {
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
const ctx = canvas.getContext('2d')
ctx.rect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = '#fff'
ctx.fill()
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
ctx.fillRect(img.position[0], img.position[1], img.position[2], img.position[3])
ctx.drawImage(invitationImg, img.position[0], img.position[1], img.position[2], img.position[3])
const base64 = canvas.toDataURL('image/jpeg', 0.3)
resList.push(base64)
})
return resList
},
err => {
Toast.fail('海报生成失败')
},
)
}
onMounted(async () => {
const data = {
posterType,
}
const posterListRes = await axios.get(
`配置文件路径/配置文件.json?t=${+new Date()}`,
)
apiService.getQrdoceUrl({ data }).then(res => {
if (res.code === '0') {
const { url } = res.data
QRCode.toDataURL(url, (err, qrCodeData) => {
if (err) Toast.fail(err)
const posterList = posterListRes.data
canvasPoster(posterList[posterType], qrCodeData).then(imgList => {
posterImage.value = imgList
})
})
}
})
})
{
"type1": [
{
"url": "海报链接11",
"position": [132, 536, 484, 484]
}
{
"url": "海报链接12",
"position": [132, 536, 484, 484]
}
],
"type1": [
{
"url": "海报链接21",
"position": [570, 1152, 138, 138]
}
{
"url": "海报链接22",
"position": [570, 1152, 138, 138]
}
],
"desc": "注: 该字段并无实际意义,主要用来在配置时用来说明配置方法,防止忘记;type1: 海报类型1,type2: 海报类型2;position:[二维码左上角X坐标,二维码左上角Y坐标,二维码宽,二维码高]"
}