先看图 (c+v即可运行,把引入的接口文件改成自己的就行)
父组件
<template>
<view class="Shareposter-view"
style="background-image:url('https://upfile168.oss-cn-beijing.aliyuncs.com/resources/6a2ccfcb6df877a2c36fcea177927cb.png');background-size:100% 100%!important;">
<!-- 中间白色背景部分 -->
<view class="Shareposter-box">
<view class="wrap">
<swiper @change="change">
<swiper-item v-for="item in rotation" :key="item.id">
<image :src="item.imageUrl"></image>
</swiper-item>
</swiper>
</view>
<view class="Shareposter-box-top">
<div class="Shareposter-box-center">
<p>1.微信扫码</p>
<p>2.下载吨力网 APP</p>
</div>
<div class="Shareposter-box-ma">
<image :src="image" mode="widthFix"></image>
</div>
</view>
</view>
<!-- 下面图标部分 -->
<view class="Shareposter-bottom">
<!-- 按钮 -->
<view class="Shareposter-bottom-button">
<!-- <button style="background-color:#FFCD1F;">复制链接</button> -->
<!-- #ifdef APP-PLUS -->
<button @click="share_qrcode2()" style="background-color:#FF6C30;">保存相册</button>
<!-- <button@click="share_qrcode()" ref="123" style="background-color:#FF6C30;">分享海报</button> -->
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<!-- <button data-name="shareBtn" open-type="share" style="background-color:#FF6C30;">分享海报</button> -->
<button @click="share_qrcode2()" style="background-color:#FF6C30;">保存相册</button>
<!-- #endif -->
<!-- #ifdef H5 -->
<button data-name="shareBtn" open-type="share" style="background-color:#FF6C30;">保存相册</button>
<!-- #endif -->
<!-- <button @click="handledownload" style="background-color:#FD302D;">保存相册</button> -->
<button @click="share_qrcode()" ref="123" style="background-color:#FD302D;">分享海报</button>
</view>
<!-- 文字 -->
<view class="Shareposter-bottom-text">
<p>*好友下载APP并使用您的邀请码成功注册后,Ta将成为您的粉丝, 将来Ta产生的订单佣金您都有奖励哦! </p>
</view>
</view>
<XqGeneratePoster ref="draw" />
</view>
</template>
<script>
import configs from "@/config/config";
import XqGeneratePoster from '@/uni_modules/XQ-GeneratePoster/components/XQ-GeneratePoster/XQ-GeneratePoster.vue';
import {
getbannerNew,
getbanners
} from "@/api/authenticationInFormation.js";
import storage from "@/utils/storage";
export default {
components: { XqGeneratePoster },
data() {
return {
current:0,
option: {
//绘制海报内容
codeUrl: '', //小程序太阳码
// codeUrl: '/static/tym.jpg', //小程序太阳码
coverUrl: '', //题库图片
headUrl: 'https://upfile168.oss-cn-beijing.aliyuncs.com/resources/6a2ccfcb6df877a2c36fcea177927cb.png', //头像
bgUrl: '', //图片背景填充,和 fillStyle 只能传一个,bgUrl 优先级高于 fillStyle
fillStyle: '#fff', //纯色背景填充颜色
nickName: 'UNDEFINED', //授权登录的用户名
miniName: '答题考试助手', //小程序名称
tkName:
'题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称题库名称~~', //题库名称
tkAuthor:
'题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者题库作者', //题库作者
tkType: '', //题库类型
cost: '', //是否需要收费 免费/付费
isPub: '' //公开 还是 私有
},
configs,
show: true,
image: "",
rotation: [],
e:0
};
},
onLoad(options) {
// #ifdef MP-WEIXIN
wx.showShareMenu({
withShareTicket: true,
menus: ["shareAppMessage", "shareTimeline"]
})
// #endif
},
onShow() {
this.getList();
this.getBanner()
},
methods: {
change(e){
this.e = e;
console.log(e)
if(e.detail.current==1){
console.log("11")
this.option.coverUrl = this.rotation[1].imageUrl
} else if(e.detail.current==2){
console.log("22")
this.option.coverUrl = this.rotation[2].imageUrl
} else if(e.detail.current==3){
console.log("33")
this.option.coverUrl = this.rotation[3].imageUrl
} else if(e.detail.current==4){
console.log("44")
this.option.coverUrl = this.rotation[4].imageUrl
} else{
console.log("00")
this.option.coverUrl = this.rotation[0].imageUrl
}
},
share_qrcode() {
console.log(111111, this.$refs);
this.$refs.draw.share_qrcode(this.option, 0);
// this.$nextTick(function() {
// });
},
share_qrcode2() {
console.log(111111, this.$refs);
this.$refs.draw.share_qrcode(this.option, 1);
},
getList() {
const memberId = storage.getUserInfo().id || ""
// console.log(memberId, "111")
getbannerNew(memberId).then((res) => {
this.image = res.data.result;
this.option.codeUrl = res.data.result;
});
},
getBanner(){
getbanners().then((res)=>{
this.rotation = res.data.result;
console.log(this.rotation,'this.rotation')
this.option.coverUrl = this.rotation[0].imageUrl
})
}
}
}
</script>
<style lang="scss">
page,
.Shareposter-view {
height: 100%;
width: 100%;
}
// <!-- 中间白色背景部分 -->
.Shareposter-box {
height: 69%;
width: 88%;
margin-left: 6%;
border-radius: 20rpx;
background-color: #fff;
padding-top: 15px;
background-size: 100% 100%;
.wrap {
height: 80%;
width: 100%;
}
image {
height: 100%;
width: 100%;
}
swiper {
height: 100%;
width: 100%;
}
}
.Shareposter-box-top {
display: flex;
justify-content: space-between;
height: 21%;
}
.Shareposter-box-ma {
width: 30%;
height: 30%;
image {
width: 80%;
}
}
.Shareposter-box-center {
padding-left: 40rpx;
p {
color: #808080;
margin-top: 10%;
}
}
// 中间邀请码部分
.Shareposter-center {
display: flex;
justify-content: space-between;
width: 88%;
height: 6%;
margin-left: 6%;
margin-top: 5%;
border-radius: 60rpx;
background-color: #fff;
.Shareposter-center-text {
font-size: 30rpx;
line-height: 250%;
padding-left: 5%;
}
.Shareposter-center-button {
width: 25%;
margin-right: 2%;
margin-top: 1%;
}
.Shareposter-center-button button {
border-radius: 50rpx;
background-color: black;
color: #fff;
height: 90%;
width: 100%;
line-height: 250%;
font-size: 10px;
}
}
// <!-- 下面图标部分 -->
.Shareposter-bottom {
width: 96%;
height: 16%;
margin-left: 2%;
margin-top: 15%;
background-color: #fff;
box-shadow: 1px 3px 14px -9px;
border-radius: 15rpx;
.Shareposter-bottom-button {
display: flex;
padding-top: 3%;
}
.Shareposter-bottom-button button {
color: #fff;
font-size: 15rpx;
width: 25%;
height: 30px;
border-radius: 50rpx;
}
.Shareposter-bottom-text {
padding-left: 3%;
padding-top: 3%;
width: 94%;
font-size: 10px;
line-height: 18px;
color: #9B9B9B;
}
}
</style>
子组件
<template>
<view class="content">
<u-mask :show="share_qrcode_flag" :zoom="false" :custom-style="{ background: 'rgba(0,0,0,.8)' }" :duration="0">
<view class="sq_box">
<view class="tz_box qrcode_box">
<view class="close_box" @click="escFlag()"><u-icon name="close" color="#ffffff"></u-icon></view>
<view class="share_qrcode">
<canvas canvas-id="myCanvas" style="width: 690px;height:1040px; position: fixed;top: -10000px;"></canvas>
<image @longpress="longpressFn()" style="width: 100%; height: 100%;" :src="canvasToTempFilePath"></image>
</view>
</view>
</view>
<view class="xqtitle">
{{text}}
</view>
</u-mask>
<u-modal
v-model="showSaveImgWin"
content="确定要保存图片吗"
@confirm="saveShareImg(canvasToTempFilePath)"
@cancel="showSaveImgWin = false"
:show-cancel-button="true"
></u-modal>
</view>
</template>
<script>
export default {
name: 'XQGeneratePoster',
data() {
return {
text: '',
ratio: 1,
ctx: null, // 创建canvas对象
canvasToTempFilePath: null, // 保存最终生成的导出的图片地址
openStatus: true, // 声明一个全局变量判断是否授权保存到相册
share_qrcode_flag: false,
showSaveImgWin: false ,//保存图片到相册,
btnType: null
};
},
methods: {
escFlag(){
this.share_qrcode_flag = false;
this.canvasToTempFilePath = null
},
longpressFn() {
if (this.btnType == 0) {
this.shareWx();
} else {
this.showSaveImgWin = true
}
},
shareWx() {
console.log('shareWx');
// #ifdef APP-PLUS
uni.share({
provider: "weixin",
scene: "WXSceneSession",
type: 2,
imageUrl: this.canvasToTempFilePath,
success: function (res) {
console.log("sharesuccess:" + JSON.stringify(res));
},
fail: function (err) {
console.log("sharefail:" + JSON.stringify(err));
}
});
// #endif
// #ifdef MP-WEIXIN
wx.showShareImageMenu({ //分享给朋友
path: this.canvasToTempFilePath,
success: (res) => {
console.log("分享成功:", res);
},
fail: (err) => {
console.log("分享失败:", err);
wx.showToast({
title: "分享失败",
duration: 2000
})
},
})
// #endif
},
share_qrcode(option, btnType) {
this.btnType = btnType;
if (btnType == 0) {
this.text = '长按海报立即分享微信好友'
} else {
this.text = '长按海报保存'
}
console.log("share_qrcode")
if (option) {
if (!this.canvasToTempFilePath) {
this.createCanvasImage(option);
}
this.share_qrcode_flag = true;
}
},
//获取图片信息
downloadFileImg(url) {
return new Promise(resolve => {
uni.getImageInfo({
src: url,
success: res => {
resolve(res.path);
},
fail: err => {
console.log(err);
uni.showToast({
title: '网络错误请重试',
icon: 'loading'
});
}
});
});
},
// 生成海报
async createCanvasImage(option) {
// 点击生成海报数据埋点
if (!this.ctx) {
uni.showLoading({
title: '生成中...'
});
let code = this.downloadFileImg(option.codeUrl);
let cover = this.downloadFileImg(option.coverUrl);
let headImg = this.downloadFileImg(option.headUrl);
let bgUrl = '';
if (option.bgUrl) {
bgUrl = new Promise(resolve => {
uni.downloadFile({
//生成临时地址
url: option.bgUrl,
success: res => {
resolve(res.tempFilePath);
},
fail: erros => {
uni.showToast({
title: '网络错误请重试',
icon: 'loading'
});
}
});
});
}
Promise.all([headImg, code, cover, bgUrl]).then(result => {
const ctx = uni.createCanvasContext('myCanvas', this);
let canvasWidthPx = 640 * this.ratio,
canvasHeightPx = 1040 * this.ratio,
avatarurl_width = 0, //绘制的头像宽度
avatarurl_heigth = 0, //绘制的头像高度
avatarurl_x = 40, //绘制的头像在画布上的位置
avatarurl_y = 28, //绘制的头像在画布上的位置
codeurl_width = 180, //绘制的二维码宽度
codeurl_heigth = 180, //绘制的二维码高度
codeurl_x = 420, //绘制的二维码在画布上的位置
codeurl_y = 800, //绘制的二维码在画布上的位置
coverurl_width = 700* this.ratio, //绘制的封面宽度
coverurl_heigth = 750, //绘制的封面高度
coverurl_x = 0, //绘制的封面在画布上的位置
coverurl_y = 0; //绘制的封面在画布上的位置
if (option.bgUrl) {
ctx.drawImage(result[3], 0, 0, 690, 1040); // 背景图片需要本地
} else {
//绘制圆角矩形
ctx.save();
ctx.translate(0, 0);
//绘制圆角矩形的各个边
this.drawRoundRectPath(ctx, 690, 1040, 14);
ctx.fillStyle = option.fillStyle || '#0688ff'; //若是给定了值就用给定的值否则给予默认值
ctx.fill();
ctx.restore();
}
// // 白底
// ctx.setFillStyle('#ffffff');
// ctx.fillRect(25, 175, 640, 840);
ctx.save(); // 先保存状态 已便于画完圆再用
ctx.beginPath(); //开始绘制
//先画个圆 前两个参数确定了圆心 (x,y) 坐标 第三个参数是圆的半径 四参数是绘图方向 默认是false,即顺时针
ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math.PI * 2, false);
ctx.clip(); //画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
ctx.drawImage(result[0], avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth); // 推进去图片
ctx.restore(); //恢复之前保存的绘图上下文状态 可以继续绘制
ctx.font = 'normal bold 45px sans-serif';
ctx.setFillStyle('#ffffff'); // 文字颜色
// if (option.nickName) {
// this.dealWords({
// ctx: ctx, //画布上下文
// fontSize: 45, //字体大小
// word: option.nickName, //需要处理的文字
// maxWidth: 480, //一行文字最大宽度
// x: 190, //文字在x轴要显示的位置
// y: 40, //文字在y轴要显示的位置
// maxLine: 1 //文字最多显示的行数
// });
// }
ctx.setFillStyle('#ffffff'); // 文字颜色
ctx.setFontSize(30); // 文字字号
ctx.fillText(option.miniName, 190, 130); // 绘制文字
ctx.setFillStyle('#222222');
if (option.tkName) {
this.dealWords({
ctx: ctx, //画布上下文
fontSize: 32, //字体大小
word: option.tkName, //需要处理的文字
maxWidth: 610, //一行文字最大宽度
x: 40, //文字在x轴要显示的位置
y: 550, //文字在y轴要显示的位置
maxLine: 2 //文字最多显示的行数
});
}
ctx.font = 'normal normal 26px sans-serif';
ctx.setFillStyle('#555555'); // 文字颜色
ctx.fillText('题库作者:', 40, 670); // 绘制文字
ctx.font = 'normal normal 26px sans-serif';
ctx.setFillStyle('#555555'); // 文字颜色
if (option.tkAuthor) {
this.dealWords({
ctx: ctx, //画布上下文
fontSize: 26, //字体大小
word: option.tkAuthor, //需要处理的文字
maxWidth: 490, //一行文字最大宽度
x: 170, //文字在x轴要显示的位置
y: 630, //文字在y轴要显示的位置
maxLine: 1 //文字最多显示的行数
});
}
// 白底
ctx.setFillStyle('#CBE6FE');
ctx.fillRect(40, 690, 125, 40);
ctx.font = 'normal normal 24px sans-serif';
ctx.setFillStyle('#1F8DFE'); // 文字颜色
ctx.fillText(option.tkType, 78, 720); // 绘制文字
// 白底
ctx.setFillStyle('#FDE5D2');
ctx.fillRect(180, 690, 125, 40);
ctx.setFillStyle('#F37F26'); // 文字颜色
ctx.fillText(option.cost, 218, 720); // 绘制文字
// 白底
ctx.setFillStyle('#D2F1EF');
ctx.fillRect(320, 690, 125, 40);
ctx.setFillStyle('#2EBBB4'); // 文字颜色
ctx.fillText(option.isPub, 360, 720); // 绘制文字
ctx.beginPath();
// 设置线宽
ctx.lineWidth = 1;
// 设置间距(参数为无限数组,虚线的样式会随数组循环)
ctx.setLineDash([10, 10]);
// 移动画笔至坐标 x20 y20 的位置
ctx.moveTo(30, 760);
// 绘制到坐标 x20 y100 的位置
ctx.lineTo(660, 760);
// 填充颜色
ctx.strokeStyle = '#aaaaaa';
// 开始填充
ctx.stroke();
ctx.closePath();
ctx.font = 'normal normal 36px sans-serif';
ctx.setFillStyle('#E65449'); // 文字颜色
ctx.fillText('1.微信扫码', 30, 870); // 绘制孩子百分比
ctx.font = 'normal normal 36px sans-serif';
ctx.setFillStyle('#222222'); // 文字颜色
ctx.fillText('2.下载吨利网', 30, 920); // 绘制孩子百分比
ctx.drawImage(result[2], coverurl_x, coverurl_y, coverurl_width, coverurl_heigth); // 绘制封面
ctx.drawImage(result[1], codeurl_x, codeurl_y, codeurl_width, codeurl_heigth); // 绘制头像
ctx.draw(false, () => {
// canvas画布转成图片并返回图片地址
uni.canvasToTempFilePath(
{
canvasId: 'myCanvas',
width: 690,
height: 1040,
destWidth: 690,
destHeight: 1040,
success: res => {
this.canvasToTempFilePath = res.tempFilePath;
this.showShareImg = true;
uni.showToast({
title: '绘制成功'
});
},
fail: err => {
uni.showToast({
title: '绘制失败'
});
},
complete: () => {
uni.hideLoading();
uni.hideToast();
}
},
this
);
});
});
}
},
drawRoundRectPath(cxt, width, height, radius) {
cxt.beginPath(0);
//从右下角顺时针绘制,弧度从0到1/2PI
cxt.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
//矩形下边线
cxt.lineTo(radius, height);
//左下角圆弧,弧度从1/2PI到PI
cxt.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
//矩形左边线
cxt.lineTo(0, radius);
//左上角圆弧,弧度从PI到3/2PI
cxt.arc(radius, radius, radius, Math.PI, (Math.PI * 3) / 2);
//上边线
cxt.lineTo(width - radius, 0);
//右上角圆弧
cxt.arc(width - radius, radius, radius, (Math.PI * 3) / 2, Math.PI * 2);
//右边线
cxt.lineTo(width, height - radius);
cxt.closePath();
},
//处理文字多出省略号显示
dealWords(options) {
options.ctx.setFontSize(options.fontSize); //设置字体大小
let allRow = Math.ceil(options.ctx.measureText(options.word).width / options.maxWidth); //实际总共能分多少行
let count = allRow >= options.maxLine ? options.maxLine : allRow; //实际能分多少行与设置的最大显示行数比,谁小就用谁做循环次数
let endPos = 0; //当前字符串的截断点
for (let j = 0; j < count; j++) {
let nowStr = options.word.slice(endPos); //当前剩余的字符串
let rowWid = 0; //每一行当前宽度
if (options.ctx.measureText(nowStr).width > options.maxWidth) {
//如果当前的字符串宽度大于最大宽度,然后开始截取
for (let m = 0; m < nowStr.length; m++) {
rowWid += options.ctx.measureText(nowStr[m]).width; //当前字符串总宽度
if (rowWid > options.maxWidth) {
if (j === options.maxLine - 1) {
//如果是最后一行
options.ctx.fillText(nowStr.slice(0, m - 1) + '...', options.x, options.y + (j + 1) * 40); //(j+1)*18这是每一行的高度
} else {
options.ctx.fillText(nowStr.slice(0, m), options.x, options.y + (j + 1) * 40);
}
endPos += m; //下次截断点
break;
}
}
} else {
//如果当前的字符串宽度小于最大宽度就直接输出
options.ctx.fillText(nowStr.slice(0), options.x, options.y + (j + 1) * 40);
}
}
},
// 保存到系统相册
saveShareImg(canvasToTempFilePath) {
console.log("saveShareImg")
uni.saveImageToPhotosAlbum({
filePath: canvasToTempFilePath,
success: () => {
this.$u.toast('保存成功,快去分享到朋友圈吧~');
},
fail: () => {
this.$u.toast('保存失败~');
}
});
}
}
};
</script>
<style lang="scss">
.xqtitle {
position: fixed;
bottom: 40rpx;
left: 30rpx;
color: #fff;
}
.sq_box {
display: flex;
height: 100%;
justify-content: center;
color: #333333;
padding: 60rpx;
.tz_box {
background-color: #ffffff;
border-radius: 14rpx;
padding: 40rpx;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
width: 100%;
text-align: center;
line-height: 50rpx;
height: 80vh;
.close_box {
position: absolute;
left: 50%;
bottom: -100rpx;
border: 4rpx solid #ffffff;
width: 60rpx;
height: 60rpx;
margin-left: -30rpx;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.share_qrcode {
width: 100%;
height: 100%;
}
.tz_title {
font-size: 34rpx;
font-weight: 600;
}
.content_box {
margin: 50rpx 0;
text-align: left;
.inf {
font-weight: bold;
font-size: 36rpx;
.copy {
font-weight: 0;
color: rgb(85, 104, 147);
font-size: 30rpx;
}
}
}
}
.qrcode_box {
padding: 0;
}
}
</style>