加粗样式uniapp多端生成带二维码海报并保存至相册的实现
在微信小程序开发中,我们常常会遇到生成带有二维码的海报并保存到手机相册的需求,比如分享活动海报、产品宣传海报等。今天就来和大家分享一下如何通过代码实现这一功能。
准备工作 在开始之前,我们需要先安装 weapp-qrcode 库,使用 npm install weapp-qrcode
命令即可完成安装。这个库将帮助我们方便地生成二维码。

- 代码实现
- 模板部分
html
<template>
<!-- 海报容器 -->
<view class="poster-container" :style="{height:getClientHeight(0)}">
<canvas id="posterCanvas" canvas-id="posterCanvas" type="2d"
:style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
<canvas style="position:absolute;left:-999px;" :style="{width:canvasWidth*0.29+'px',height:canvasWidth*0.29+'px'}"
canvas-id="qrcodeCanvas" id="qrcodeCanvas"></canvas>
<!-- 统一保存按钮 -->
<button class="save-btn" @click="handleSave" :loading="btnLoading">保存到手机相册</button>
</view>
</template>
在模板中,我们定义了一个海报容器,包含两个 canvas
元素,一个用于绘制海报内容,另一个用于生成二维码。同时,还有一个保存按钮,点击该按钮会触发 handleSave 方法来保存海报到相册。
- 脚本部分
typescript
```typescript
<script setup lang="ts">
import QRCode from 'weapp-qrcode'
import { getClientHeight } from '@/core/utils/verulia'
const btnLoading = ref(false);
const canvasWidth = ref(sysInfo.screenWidth)
const canvasHeight = ref(canvasWidth.value * 1.56)
const { proxy } = getCurrentInstance() || {}
if (!proxy) {
throw new Error('组件实例未找到')
}
//需要渲染到画布上的数据
const xxxInfo = ref(null)
let canvas
// 生成二维码图片路径
const generateQrcode = (text : string) => {
return new Promise<string>((resolve, reject) => {
try {
const qrWidth = canvasWidth.value * 0.29;
const qr = new QRCode({
width: qrWidth,
height: qrWidth,
canvasId: 'qrcodeCanvas',
text,
correctLevel: 3
})
setTimeout(() => {
uni.canvasToTempFilePath({
canvasId: 'qrcodeCanvas',
success: res => {
resolve(res.tempFilePath)
},
fail: err => {
uni.hideLoading()
console.error('二维码生成失败:', err)
reject(null)
}
})
}, 500)
} catch (e) {
console.log(e)
reject(e)
}
})
}
const loadWxImage = (path : string, canvas : any) => {
return new Promise<HTMLImageElement>((resolve, reject) => {
const img = canvas.createImage()
img.onload = () => resolve(img)
img.onerror = reject
img.src = path
})
}
// 微信小程序绘图处理
const drawWeixinPoster = async () => {
return new Promise((resolve, reject) => {
try {
console.log('准备获取 canvas 节点,id 为:', 'posterCanvas')
uni.createSelectorQuery().select('#posterCanvas').fields({ node: true, size: true })
.exec(async (res) => {
console.log('获取 canvas 节点结果:', res)
if (!res?.[0]?.node || JSON.stringify(res?.[0]?.node) === '{}') {
uni.hideLoading()
reject('获取canvas节点失败')
return
} else {
canvas = res[0].node
console.log('成功获取到 canvas 节点:', canvas)
}
const ctx = canvas.getContext('2d')
if (!ctx) {
reject('获取绘图上下文失败')
return
}
//获取图片 微信小程序中需要这样转一下
const [bgImage, orImage, deImage] = await Promise.all([
loadWxImage("图片路径", canvas),
loadWxImage("图片路径", canvas),
loadWxImage("图片路径", canvas)
])
let text = "二维码内容";
let qrcodePath, qrImage
if (text) {
qrcodePath = await generateQrcode(String(text))
qrImage = await new Promise<any>((resolve) => {
const img = canvas.createImage()
img.onload = () => resolve(img)
img.src = qrcodePath
})
} else {
//这里重新生成了一下,防止生成二维码失败
setTimeout(async () => {
let text1 = "二维码内容";
if (text1) {
qrcodePath = await generateQrcode(String(text1))
qrImage = await new Promise<any>((resolve) => {
const img = canvas.createImage()
img.onload = () => resolve(img)
img.src = qrcodePath
})
} else {
reject(null)
uni.hideLoading()
}
}, 1000)
}
const dpr = sysInfo.pixelRatio
canvas.width = canvasWidth.value * dpr
canvas.height = canvasHeight.value * dpr
ctx.scale(dpr, dpr)
//中间这里写你要在画布上要画的其他内容、根据自己需要改就可以,可以参考微信小程序/uniapp的官方文档
//将二维码画上去
ctx.drawImage(qrImage, canvasWidth.value * 0.29, canvasWidth.value * 0.915, canvasWidth.value * 0.42, canvasWidth.value * 0.42)
uni.hideLoading()
resolve(true)
})
} catch (e) {
uni.hideLoading()
console.log(e)
reject(e)
}
})
}
//安卓端绘制方法
const drawAppPoster = async () => {
return new Promise(async (resolve, reject) => {
try {
console.log('准备获取 canvas 节点,id 为:', 'posterCanvas')
const ctx = uni.createCanvasContext("posterCanvas",)
if (!ctx) {
reject('获取绘图上下文失败')
return
}
let text = "二维码内容"
let qrcodePath
if (text) {
qrcodePath = await generateQrcode(String(text))
} else {
setTimeout(async () => {
let text1 = "二维码内容";
if (text1) {
qrcodePath = await generateQrcode(String(text1))
} else {
reject(null)
uni.hideLoading()
}
}, 1000)
}
const dpr = sysInfo.pixelRatio
//中间这里写你要在画布上要画的其他内容、根据自己需要改就可以,可以参考微信小程序/uniapp的官方文档
//将二维码画上去
await ctx.drawImage(qrcodePath, canvasWidth.value * 0.29, canvasWidth.value * 0.915, canvasWidth.value * 0.42, canvasWidth.value * 0.42)
ctx.draw();
uni.hideLoading()
resolve(true)
} catch (e) {
uni.hideLoading()
console.log(e)
reject(e)
}
})
}
//保存下载二维码
const handleSave = async () => {
btnLoading.value = true;
try {
if (uni.getSystemInfoSync().platform === 'android') {
// #ifdef APP-PLUS
// 安卓端使用 plus.android.requestPermissions 请求权限
const Context = plus.android.importClass("android.content.Context");
const PackageManager = plus.android.importClass("android.content.pm.PackageManager");
const main = plus.android.runtimeMainActivity();
const pm = main.getPackageManager();
const permission = "android.permission.WRITE_EXTERNAL_STORAGE";
const hasPermission = pm.checkPermission(permission, main.getPackageName()) === PackageManager.PERMISSION_GRANTED;
if (!hasPermission) {
plus.android.requestPermissions([permission], function (resultObj) {
const result = resultObj.granted;
if (result.indexOf(permission) !== -1) {
console.log('已获取相册写入权限');
// 在这里可以执行保存图片到相册的操作
uni.canvasToTempFilePath({
canvasId: 'posterCanvas',
success: function (res) {
console.log(res.tempFilePath)
uni.saveImageToPhotosAlbum({ filePath: res.tempFilePath })
uni.showToast({ title: '保存成功' })
btnLoading.value = false;
},
fail: function (e) {
console.log(e);
btnLoading.value = false;
}
})
} else {
console.log('用户拒绝授予相册写入权限');
// 可以引导用户手动开启权限
uni.showModal({
title: '权限提示',
content: '请在设置中开启相册写入权限,以便保存图片到相册。',
success: (res) => {
if (res.confirm) {
// 打开设置页面
btnLoading.value = false;
uni.openSetting();
}
}, fail: (err) => {
console.log(err);
btnLoading.value = false;
}
});
}
}, function (error) {
console.error('请求权限出错:', error);
btnLoading.value = false;
});
} else {
console.log('已有相册写入权限');
// 在这里可以执行保存图片到相册的操作
uni.canvasToTempFilePath({
canvasId: 'posterCanvas',
success: function (res) {
console.log(res.tempFilePath)
uni.saveImageToPhotosAlbum({ filePath: res.tempFilePath })
uni.showToast({ title: '保存成功' });
btnLoading.value = false;
},
fail: function (e) {
console.log(e);
btnLoading.value = false;
}
})
}
// #endif
//#ifdef MP-WEIXIN
// 1. 检查权限
//安卓端微信小程序端保存下载
const { authSetting } = await uni.getSetting();
if (!authSetting['scope.writePhotosAlbum']) {
btnLoading.value = false;
await uni.authorize({ scope: 'scope.writePhotosAlbum' });
}
await uni.canvasToTempFilePath({
canvas: canvas,
success: function (res) {
// 在H5平台下,tempFilePath 为 base64
console.log(res.tempFilePath)
uni.saveImageToPhotosAlbum({ filePath: res.tempFilePath });
uni.showToast({ title: '保存成功' });
btnLoading.value = false;
}, fail: function (e) {
btnLoading.value = false;
console.log(e);
}
});
//#endif
} else {
//ios端及ios端微信小程序处理
// 1. 检查权限
const { authSetting } = await uni.getSetting();
if (!authSetting['scope.writePhotosAlbum']) {
btnLoading.value = false;
await uni.authorize({ scope: 'scope.writePhotosAlbum' });
}
await uni.canvasToTempFilePath({
canvas: canvas,
success: function (res) {
// 在H5平台下,tempFilePath 为 base64
console.log(res.tempFilePath)
uni.saveImageToPhotosAlbum({ filePath: res.tempFilePath });
uni.showToast({ title: '保存成功' });
btnLoading.value = false;
}, fail: function (e) {
console.log(e);
btnLoading.value = false;
}
});
}
} catch (e) {
console.log(e);
btnLoading.value = false;
}
};
onReady(async () => {
const xxxId = uni.getStorageSync("xxxId");
uni.showLoading({
mask: true
})
try {
//这里要获取一下你的数据内容,请求接口这里只是示例
const res = await api.getInfo(xxxId);
xxxInfo.value = res.data
await nextTick()
setTimeout(async () => {
// #ifdef MP-WEIXIN
await drawWeixinPoster();
// #endif
// #ifdef APP-VUE
await drawAppPoster()
// #endif
}, 1000)
} catch (error) {
uni.hideLoading()
console.error('数据请求失败:', error)
}
})
</script>
- 样式部分
css
<style>
/* 统一样式 */
.poster-container {
background: #fff;
}
.save-btn {
margin-top: 40rpx;
width: 80%;
height: 80rpx;
line-height: 80rpx;
background: #3366FF;
color: white;
border-radius: 40rpx;
}
#posterCanvas {
width: 100%;
height: auto;
}
</style>