一、首先看一下效果,点击分享生成海报,弹出海报,包括一些信息,图片以及小程序二维码,长按可保存到本地;
![](https://i-blog.csdnimg.cn/direct/2113e9238c8741dcb3bd6e4632fc0782.png)
二、下面为封装好的生成海报的组件;
<template>
<view class="container">
<!-- 生成海报的canvas -->
<view class="my-canvas-box" @touchmove.stop.prevent :class="posterInfo.status ? 'show' : 'hide'" @click="posterInfo.status = false">
<canvas class="my-canvas" canvas-id="myCanvas" @longpress.stop="saveSharePic"></canvas>
<view class="canvas-tip">长按可保存海报</view>
</view>
</view>
</template>
<script>
export default {
name: 'PosterGenerator',
props:{
shopInfo: {
type: Object,
},
operatemerId:{
type:String,
},
},
data() {
return {
// 用来控制canvas遮罩层的显示与隐藏
posterInfo: {
status: false,
},
// shopInfo: {},
bufferImg: '',
token: '',
codeImg: '',
};
},
created() {
this.token = uni.getStorageSync('token');
},
methods: {
// 获取 access_token
async getAccessToken() {
const appid = 'wx0f1dc3df604bd298';
const secret = '65db0115543850fd3deca0e23de1961f';
const result = await uni.request({
method: "GET",
url: `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`
});
const access_token = result.data.access_token;
// 获取到 access_token 后获取二维码
// 注意 access_token 参数是必须放在url后面 其余参数 要在data里面
const qrResult = await uni.request({
method: "POST",
responseType: 'arraybuffer', // 注意一定要加 不然返回的Buffer流会乱码 导致无法转base64
url: `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${access_token}`,
data: {
page: 'subPages/shop-detail/shop-detail',// 需要打开的页面路径
scene: this.operatemerId,// 这个是需要传递的参数
}
});
// 拿到buffer流 通过wx.arrayBufferToBase64 转成base64 在页面展示
// 如果请求时不加 responseType: 'arraybuffer' 拿到的buffer流转码会失败
this.bufferImg = "data:image/png;base64," + wx.arrayBufferToBase64(qrResult.data);
// 将bse64图片上传给后端,获取到网络地址
this.bindUploadBase64(this.bufferImg);
},
// 上传二维码图片到后端
async bindUploadBase64(base64Img) {
// 将Base64 编码的图片数据转换为一个临时文件路径
const filePath = this.base64ToFilePath(base64Img);
if (filePath) {
const uploadResult = await uni.uploadFile({
url: this.app.api_url + '/file/upload',
filePath: filePath,
name: "file",
header: {
"content-type": "multipart/form-data",
'Authorization': 'Bearer ' + this.token
}
});
const tip = JSON.parse(uploadResult.data);
if (tip.code == 0) {
this.codeImg = tip.data.url;
} else {
console.log('上传失败');
}
}
},
// 将Base64 编码的图片数据转换为一个临时文件路径
base64ToFilePath(base64Img) {
console.log('base64Img',base64Img)
console.log('base64Img.replace',base64Img.replace(/^data:image\/\w+;base64,/, ""),)
const filePath = `${wx.env.USER_DATA_PATH}/temp_image.png`;
wx.getFileSystemManager().writeFile({
filePath: filePath,
data: base64Img.replace(/^data:image\/\w+;base64,/, ""),
encoding: 'base64',
success: () => {
console.log('文件写入成功', filePath);
},
fail: (err) => {
console.error('文件写入失败', err);
}
});
return filePath;
},
async generatePoster() {
await this.getAccessToken();
const user = uni.getStorageSync('user');
// 这里是创建 canvas 绘图上下文
const context = uni.createCanvasContext('myCanvas',this);
// 这里可以根据自己的需求显示加载动画
uni.showToast({
title: '正在生成海报,请稍后',
icon: 'none',
duration: 3000
});
// 给整个canvas填充白色背景(这个如果不加上的话,在APP端生成的海报没有白色背景)
context.setFillStyle('#ffffff');
context.fillRect(0, 0, 500, 900);
context.draw();
// 绘制用户昵称
context.setFontSize(14);
context.setFillStyle('#000000');
// 这里根据自己的项目填写用户昵称的字段
context.fillText(user.username, 100, 48);
context.setFontSize(12);
context.setFillStyle('#999999');
context.fillText('的店铺分享', 100, 68);
this.drawText(this.shopInfo.merchantsName, 20, 379, context);
// 绘制头像
// url: '用户的头像地址(一定要是网络路径)',
const avatarRes = await uni.downloadFile({ url: user.avatar });
context.save();
// 这个就是绘制圆形头像
context.beginPath();
context.arc(50, 50, 30, 0, 2 * Math.PI);
context.clip();
context.drawImage(avatarRes.tempFilePath, 20, 20, 60, 60);
context.restore();
context.draw(true);
// 绘制商店详情
const shopImgRes = await uni.downloadFile({ url: this.shopInfo.imgPathL[0] });
context.drawImage(shopImgRes.tempFilePath, 15, 92, 245, 245);
context.draw(true);
// 绘制二维码
// this.codeImg是上面获取到的图片
const qrImgRes = await uni.downloadFile({ url: this.codeImg });
context.drawImage(qrImgRes.tempFilePath, 165, 360, 100, 100);
context.draw(true);
// 绘制完成,让canvas显示【这里看自己项目,是否有loading动画】
this.posterInfo.status = true;
},
// 商品名称文字换行
drawText(context, x, y, canvas) {
let strArr = [];
let n = 11;
for (let i = 0, l = context.length; i < l / n; i++) {
let a = context.slice(n * i, n * (i + 1));
strArr.push(a);
}
strArr.forEach((item, index) => {
// 限制只能显示4行文字
if (index > 3) {
return;
}
y += 20;
canvas.setFontSize(12);
canvas.setFillStyle('#333333');
canvas.fillText(item, x, y);
});
},
// 长按保存生成的海报
saveSharePic() {
var that=this;
uni.showModal({
title: '提示',
content: '确定要保存分享海报吗?',
success: function(res) {
if (res.confirm) {
console.log('确认')
// canvas生成图片
uni.canvasToTempFilePath({
// 这里修改保存的图片格式
fileType: 'jpg',
canvasId: 'myCanvas',
quality: 1,
success: function(res) {
// 保存到本地
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: function() {
uni.showToast({
title: '保存海报成功',
icon: 'none',
duration: 3000
});
},
fail: function() {
uni.showToast({
title: '保存海报失败',
icon: 'none',
duration: 3000
});
}
});
},
fail: function(err) {
console.log('保存失败',err)
}
},that);
}
}
});
},
}
};
</script>
<style lang="scss" scoped>
.my-canvas-box {
width: 750rpx;
height: 100%;
position: fixed;
background-color: rgba(0, 0, 0, 0.6);
z-index: 99;
&.hide {
display: none;
}
&.show {
display: block;
}
.canvas-tip {
color: #ffffff;
font-size: 24rpx;
margin-top: 30rpx;
text-align: center;
}
/* #ifdef MP-WEIXIN */
.my-canvas {
width: 550rpx;
height: 950rpx;
background-color: #ffffff;
margin: 20rpx auto;
}
/* #endif */
/* #ifdef APP-PLUS || H5 */
.my-canvas {
width: 550rpx;
height: 950rpx;
background-color: #ffffff;
margin-top: 220rpx auto;
}
/* #endif */
}
.container {
height: 100%;
width: 100%;
}
</style>
三、在父组件中引用该组件
<view class="relay" @click="generatePoster">
<button>生成海报</button>
</view>
<!-- 生成海报组件,两个参数根据实际情况修改 -->
<PosterGenerator ref="posterGenerator" :shopInfo="shopInfo" :operatemerId="operatemerId" />
import PosterGenerator from '@/subPages/components/PosterGenerator/PosterGenerator.vue';
components:{
PosterGenerator
},
// 分享生成海报
generatePoster() {
// 调用 PosterGenerator 组件中的 generatePoster 方法
this.$refs.posterGenerator.generatePoster();
},