父组件引入邀请海报组件:
<QrcodePoster
ref="poster"
:title="title"
:subTitle="subTitle"
:headerImg="newIamge"
:price="pinkT.totalPrice"
:productPrice="productPrice"
:quota="quota"
:abImg="pinkT.avatar "
:leaderNickname="newHeaderName"
:qrcode="posterUrl"
></QrcodePoster>
//
title: "", // 海报标题
subTitle: "", // 海报副标题
headerImg "", // 商品图片
price "", // 商品现价
productPrice "", // 商品原价
quota: "", // 海报仅限x个名额
abImg: "", // 团长头像
leaderNickname: "", // 团长名字
qrcode: "", // 后端返回的二维码图片
父组件打开海报方法:
openPoster() {
this.$nextTick(() => {
this.$refs.poster.showCanvas(
"https:xxxx.png"
);
});
},
海报组件代码
<template>
<view class="content" v-if="isShow" @click.stop="isShow=false">
<canvas @click.stop="" :style="{ width: canvasW + 'px', height: canvasH + 'px' }"
canvas-id="my-canvas"></canvas>
<view class="save-btn" @click.stop="saveImage"></view>
</view>
</template>
<script>
export default {
props: {
headerImg: {
type: String,
default: ''
},
title: {
type: String,
default: '商城用户'
},
subTitle: {
type: String,
default: ''
},
price: {
type: String,
default: ''
},
abImg: {
type: String,
default: ''
},
productPrice: {
type: String,
default: ''
},
quota: {
type: String,
default: ''
},
leaderNickname: {
type: String,
default: ''
},
qrcode: {
type: String,
default: ''
}
},
data() {
return {
canvasW: 0,
canvasH: 0,
ctx: null,
isShow: false,
priceTxt: '元/人',
posterBgc: 'xxxxx.png', // 设置背景图片
leaderFlag: 'xxxx.png', // 团长标识logo
}
},
methods: {
//显示
showCanvas() {
this.isShow = true
this.__init()
},
//初始化画布
async __init() {
uni.showLoading({
title: '加载中...',
mask: true
})
// 创建画布
this.ctx = uni.createCanvasContext('my-canvas', this)
this.canvasW = uni.upx2px(571);
this.canvasH = uni.upx2px(1111);
//设置画布背景颜色透明
this.ctx.setFillStyle('#fff')
//设置画布大小
this.ctx.fillRect(0, 0, this.canvasW, this.canvasH)
// 设置背景图片
let posterImg = await this.getImageInfo(this.posterBgc)
this.ctx.drawImage(posterImg.path, 0, 0, this.canvasW, this.canvasH)
// 设置商品图片
let headerImg = await this.getImageInfo(this.headerImg)
let hW = uni.upx2px(473);
let hH = uni.upx2px(473);
// this.drawRoundImg(ctx, img, x, y, width, height, radius)
this.drawRoundImg(this.ctx, headerImg.path, ((this.canvasW - hW) / 2), ((this
.canvasW - hW) / 2 + (uni.upx2px(60))), hW, hH, (uni.upx2px(16)))
//绘制商品标题
this.ctx.beginPath()
this.ctx.setFontSize(12);
this.ctx.setFillStyle('#000');
this.ctx.font = 'normal bold 12px sans-serif';
// toFormateStr(ctx, str, draw_width, lineNum, startX, startY, steps)
this.toFormateStr(this.ctx, this.title, 170, 2, ((this.canvasW - hW) / 2), ((this.canvasW - hW) / 2 + (
uni.upx2px(564))), 18);
this.ctx.fill()
this.ctx.closePath()
//绘制副标题
this.ctx.beginPath()
this.ctx.setFontSize(12);
this.ctx.setFillStyle('#333');
this.ctx.fillText(this.subTitle, ((this.canvasW - hW) / 2), ((this.canvasW - hW) / 2 + (
uni.upx2px(650))))
this.ctx.fill()
this.ctx.closePath()
//绘制价格
this.ctx.beginPath()
this.ctx.setFontSize(29);
this.ctx.setFillStyle('#E01E1E');
this.ctx.fillText(this.price, ((this.canvasW - hW) / 2), ((this.canvasW - hW) / 2 + (
uni.upx2px(715))))
this.ctx.closePath()
// 获取价格长度
let pLength = this.computedWidth(this.price)
// 元/人
this.ctx.beginPath()
this.ctx.setFontSize(12);
this.ctx.setFillStyle('#333');
// this.ctx.fillText(this.priceTxt, (((this.canvasW - hW) / 2) + pLength), (
// ((this.canvasW - hW) / 2) + hH + 124))
this.ctx.fillText(this.priceTxt, (((this.canvasW - hW) / 2) + pLength), ((this.canvasW - hW) / 2 + (
uni.upx2px(715))))
this.ctx.closePath()
// 原价
this.ctx.beginPath()
let textWidth = this.ctx.measureText(this.productPrice).width - 9
this.ctx.setFontSize(10);
this.ctx.setFillStyle('#999');
this.ctx.fillText(this.productPrice, (((this.canvasW - hW) / 2) + pLength + (
uni.upx2px(80))), ((this.canvasW - hW) / 2 + (
uni.upx2px(715))))
this.ctx.rect((((this.canvasW - hW) / 2) + pLength + (
uni.upx2px(82))), ((this.canvasW - hW) / 2 + (
uni.upx2px(707))), textWidth, 1)
this.ctx.fill()
this.ctx.closePath()
// 仅限x个名额
this.ctx.beginPath()
this.ctx.setFontSize(12);
this.ctx.setFillStyle('#fff');
// this.ctx.fillText(this.quota, (((this.canvasW - hW) / 2) + 170), (
// ((this.canvasW - hW) / 2) + hH + 88))
this.ctx.fillText(this.quota, ((this.canvasW - hW) / 2 + (
uni.upx2px(330))), ((this.canvasW - hW) / 2 + (
uni.upx2px(648))))
this.ctx.closePath()
//团长图片
let avaImg = await this.getImageInfo(this.abImg)
// avaRound(ctx, img, x, y, radius)
this.avaRound(this.ctx, avaImg.path, (((this.canvasW - hW) / 2 + (
uni.upx2px(38)))), ((this.canvasW - hW) / 2 + (
uni.upx2px(820))), (
uni.upx2px(56)));
// 团长标识
this.ctx.beginPath()
let leaderFlagImg = await this.getImageInfo(this.leaderFlag)
this.ctx.drawImage(leaderFlagImg.path, ((this.canvasW - hW) / 2), ((this.canvasW - hW) / 2 + (
uni.upx2px(860))), (
uni.upx2px(76)), (
uni.upx2px(32)))
this.ctx.closePath()
// 团长名字
this.ctx.beginPath()
// this.ctx.font = 'bold 12px PingFangSC'
this.ctx.setFontSize(12); //设置标题字体大小
this.ctx.setFillStyle('#fff'); //设置标题文本颜色
this.ctx.fillText(this.leaderNickname, (((this.canvasW - hW) / 2 + (
uni.upx2px(222)))), ((this.canvasW - hW) / 2 + (
uni.upx2px(808))))
this.ctx.fill()
this.ctx.closePath()
//小程序码
this.ctx.beginPath()
let qrcodeImg = await this.getImageInfo(this.qrcode)
this.ctx.drawImage(qrcodeImg.path, (((this.canvasW - hW) / 2 + (
uni.upx2px(8)))), ((this.canvasW - hW) / 2 + (
uni.upx2px(910))), 50, 50)
this.ctx.closePath()
//延迟渲染
setTimeout(() => {
this.ctx.draw(true, () => {
uni.hideLoading()
})
}, 1000)
},
toFormateStr(ctx, str, draw_width, lineNum, startX, startY, steps) {
var strWidth = ctx.measureText(str).width; // 测量文本源尺寸信息(宽度)
var startpoint = startY,
keyStr = '',
sreLN = strWidth / draw_width;
var liner = Math.ceil(sreLN); // 计算文本源一共能生成多少行
let strlen = parseInt(str.length / sreLN); // 等比缩放测量一行文本显示多少个字符
// 若文本不足一行,则直接绘制,反之大于传入的最多行数(lineNum)以省略号(...)代替
if (strWidth < draw_width) {
ctx.fillText(str, startX, startpoint);
} else {
for (var i = 1; i < liner + 1; i++) {
let startPoint = strlen * (i - 1);
if (i < lineNum || lineNum == -1) {
keyStr = str.substr(startPoint, strlen);
ctx.fillText(keyStr, startX, startpoint);
} else {
keyStr = str.substr(startPoint, strlen - 5) + '...';
ctx.fillText(keyStr, startX, startpoint);
break;
}
startpoint = startpoint + steps;
}
}
},
// 获取价格长度 更好控制priceTxt的位置
computedWidth(val) {
let count = 0
let arr = String(val).split('')
arr.map(item => {
// 如果价格为数字类型
if (Number(item) || item == '0') {
count += 19
} else {
// 如果价格有小数点
count += 5
}
})
return count
},
//带圆角图片
drawRoundImg(ctx, img, x, y, width, height, radius) {
ctx.beginPath()
ctx.save()
// 左上角
ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 1.5)
// 右上角
ctx.arc(x + width - radius, y + radius, radius, Math.PI * 1.5, Math.PI * 2)
// 右下角
ctx.arc(x + width - radius, y + height - radius, radius, 0, Math.PI * 0.5)
// 左下角
ctx.arc(x + radius, y + height - radius, radius, Math.PI * 0.5, Math.PI)
// 回到原点的直线
ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI)
ctx.stroke()
ctx.clip()
ctx.drawImage(img, x, y, width, height);
ctx.restore()
ctx.closePath()
},
// 团长头像
avaRound(ctx, img, x, y, radius) {
ctx.save();
let size = 2 * radius;
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.clip();
ctx.drawImage(img, x - radius, y - radius, size, size);
ctx.restore()
},
//圆角矩形
drawRoundRect(ctx, x, y, width, height, radius, color) {
ctx.save();
ctx.beginPath();
ctx.setFillStyle(color);
ctx.setStrokeStyle(color)
ctx.setLineJoin('round'); //交点设置成圆角
ctx.setLineWidth(radius);
ctx.strokeRect(x + radius / 2, y + radius / 2, width - radius, height - radius);
ctx.fillRect(x + radius, y + radius, width - radius * 2, height - radius * 2);
ctx.stroke();
ctx.closePath();
},
//获取图片
getImageInfo(imgSrc) {
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: imgSrc,
success: (image) => {
resolve(image);
console.log('获取图片成功', image)
},
fail: (err) => {
reject(err);
console.log('获取图片失败', err)
}
});
});
},
//保存图片到相册
saveImage() {
//判断用户授权
// uni.getSetting({
// success(res) {
// // console.log('获取用户权限', res.authSetting)
// if (Object.keys(res.authSetting).length > 0) {
// //判断是否有相册权限
// if (res.authSetting['scope.writePhotosAlbum'] == undefined) {
// //打开设置权限
// uni.openSetting({
// success(res) {
// // console.log('设置权限', res.authSetting)
// }
// })
// } else {
// if (!res.authSetting['scope.writePhotosAlbum']) {
// //打开设置权限
// uni.openSetting({
// success(res) {
// // console.log('设置权限', res.authSetting)
// }
// })
// }
// }
// } else {
// return
// }
// }
// })
var that = this
uni.canvasToTempFilePath({
canvasId: 'my-canvas',
quality: 1,
complete: (res) => {
// console.log('保存到相册', res);
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success(res) {
uni.showToast({
title: '已保存到相册',
icon: 'success',
duration: 2000
})
setTimeout(() => {
that.isShow = false
}, 2000)
}
})
}
}, that);
}
}
}
</script>
<style scoped lang="scss">
.content {
position: fixed;
width: 750rpx;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, .4);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
// 保存按钮
.save-btn {
margin-top: 35rpx;
width: 501rpx;
height: 83rpx;
background: url(https://xxxx.png) no-repeat;
background-size: 100%;
}
}
</style>
注意
子组件的获取图片方法getImageInfo(),会验证图片获取成功否.
所以在开发过程中如果海报未能展示,请查看该方法,相关图片是否获取成功