示例图:


文献参考:
参考文档:https://zhuanlan.zhihu.com/p/597629702
uni-app官网:uni.createSelectorQuery() | uni-app官网
微信小程序官网:canvas | 微信开放文档
海报内容准备:
1、背景图片
2、二维码
3、用户头像,用户名称
4、简短的描述文案
PS:
1、若使用<canvas type="2d" id="myCanvas"></canvas> 则报错canvasToTempFilePath:fail fail canvas is empty。
2、若canvas不设置canvas-id,则报错<canvas>: canvas-id attribute is undefined(env: Windows,mp,1.06.2308310; lib: 2.25.3)。
3、若只设置canvas-id,则节点信息为null。
const query = uni.createSelectorQuery().in(this); query.select('#canvas').boundingClientRect(data => { console.log("节点信息" + JSON.stringify(data)); // null }).exec();
4、canvas绘制的图片不支持网络图片,因此使用网络需使用uni.getImageInfo获取图片信息。
实现
HTML页面
// 触发按钮
<view data-event-opts="{{[['tap',[['handleClick']]]]}}" bindtap="__e">生成海报</view>
// 海报容器
<canvas canvas-id="canvass" id="canvas" style="width:100%; height: 1000rpx;" />
// 生成的海报图片
<image src="{{imgAddRess}}" style="width:100%; height: 1000rpx;"></image>
JS页面
// 单网图
data: {
return {
imgAddRess: "",
shareBg: "https://img2.baidu.com/it/u=3334525604,928778682&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1703955600&t=c88ebba9f3d8aeee92c6afd219945f9a",
memberCount: 100,
userImage:"/static/img/code/kong-img.png",
userName: "快乐星球",
shareQrImageUrl: "/static/img/code/kong-img.png"
}
}
// 生成海报
handleClick() {
const _this = this;
// 因shareBg图片为线上图片,需做转换uni.getImageInfo({ src: this.shareBg })
// 单个图片处理
uni.getImageInfo({ src: this.shareBg }).then(res => {
this.shareBg = res[1].path; // 获取转换后的图片路径
this.createdCanvas().then(()=>{
// 绘制需要延迟,不然第一次加载为空白
setTimeout(() => {
// 将生成的canvas图片,转为真实图片-地址
uni.canvasToTempFilePath({
x: 0,
y: 0,
canvasId: "canvass",
success: res => {
_this.imgAddRess = res.tempFilePath;
console.log('真实图片>>>>>>', res);
},
fail: (err) => { console.log('error', err) }
},this);
}, 500);
});
});
},
handleClick2() {
const _this = this;
// 多张图片处理
const Images = [this.shareBg, this.shareQrImageUrl, this.userImage];
Promise.all(Images.map(img => uni.getImageInfo({ src: img }))).then(
imageInfos => {
console.log('Images then>>>>>>', imageInfos);
this.shareBg = imageInfos[0][1].path;
this.shareQrImageUrl = imageInfos[1][1].path;
this.userImage = imageInfos[2][1].path;
//根据参数开始绘图
this.createdCanvas().then(()=>{
// 绘制需要延迟,不然第一次加载为空白
setTimeout(() => {
// 将生成的canvas图片,转为真实图片-地址
uni.canvasToTempFilePath({
x: 0,
y: 0,
canvasId: "canvass",
success: res => {
_this.imgAddRess = res.tempFilePath;
console.log('真实图片>>>>>>', res);
},
fail: (err) => { console.log('error', err) }
},this);
}, 500);
})
}
);
}
createdCanvas(){
const _this = this;
return new Promise((resolve, reject) => {
//绘图上下文
const ctx = uni.createCanvasContext('canvass', this);
uni.createSelectorQuery()
.in(this)
.select('#canvas')
.boundingClientRect(rect => {
if(!rect) return
var width = rect.width;
var height = rect.height;
// 背景图
ctx.drawImage( _this.shareBg, 0, 0, width, height);
// 文案:百位超能大神带你飞
ctx.font = "bold 31px 'DINAlternate-Bold, DINAlternate'"
ctx.setFillStyle('#ff9900');
ctx.setTextAlign('left');
ctx.fillText(_this.memberCount, Math.ceil(width*0.32), Math.ceil(height*0.103*2)+6);
ctx.setFillStyle('#ff9900');
ctx.setTextAlign('left');
ctx.font = "16px 'PingFangSC-Medium, PingFang SC'"
ctx.fillText('位超能大神带你飞', Math.ceil(width*0.32)+66, Math.ceil(height*0.103*2));
// 二维码区背景背景
ctx.beginPath();
// r:8 ,
// 起始点
ctx.moveTo(12, height - 92 + 8);
// 左上角
ctx.arcTo(12, height - 92, 12 + 8, height - 92, 8);
// 右上角
ctx.arcTo(width - 12, height - 92, width - 12, height - 92 + 8, 8);
// 右下角
ctx.arcTo(width - 12, height - 12, width - 12 - 8, height - 12, 8);
// 左下角
ctx.arcTo(12, height - 12, 12, height - 12 - 8, 8);
ctx.fillStyle = '#ffffff';
ctx.closePath();
ctx.fill();
//用户头像+用户名
let avatarurl_width = 50; //绘制的头像宽度
let avatarurl_heigth = 50; //绘制的头像高度
let avatarurl_x = 22; //绘制的头像在画布上的位置
let avatarurl_y = height - 77; //绘制的头像在画布上的位置
ctx.save(); // 保存绘图上下文
ctx.beginPath(); // 开始创建一个路径
ctx.arc(
avatarurl_width / 2 + avatarurl_x,
avatarurl_heigth / 2 + avatarurl_y,
avatarurl_width / 2,
0,
Math.PI * 2,
false
); // 画一个圆形裁剪区域
ctx.clip(); // 裁剪
ctx.drawImage(
this.userImage,
avatarurl_x,
avatarurl_y,
avatarurl_width,
avatarurl_heigth
); // //分享人头像-绘制图片x,y轴
ctx.restore(); // 恢复之前保存的绘图上下文
// 分享userName
ctx.setFillStyle('#141414');
ctx.setTextAlign('left');
ctx.font = "bold 16px 'PingFangSC-Medium, PingFang SC'"
let title = _this.userName;
if(title.length>14){
title =title.substr(0,14)+'...'
}
ctx.fillText(title, 80, height - 55) ;
// 分享描述
ctx.setFillStyle('#666666');
ctx.setTextAlign('center');
ctx.font = "11px 'PingFangSC-Regular, PingFang SC'"
ctx.fillText('欢迎来到快乐星球根据地', 140, height - 33);ht - 33);
// 二维码图片
ctx.drawImage(_this.shareQrImageUrl, width - 84, height - 84, 64, 64 );
ctx.draw();
}).exec()
//获取节点
resolve()
})
}
多网图
data: {
return {
imgAddRess: "",
shareBg:"https://img0.baidu.com/it/u=2868999810,2001254139&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=714",
shareQrImageUrl: "https://img1.baidu.com/it/u=206225810,1732683717&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
avater: "https://img2.baidu.com/it/u=1979438230,3745650916&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500"
}
}
// 生成海报
handleSetImage() {
const _this = this;
const Images = [this.shareBg, this.shareQrImageUrl, this.avater];
let aa = Images.map(img => uni.getImageInfo({ src: img }));
Promise.all(Images.map(img => uni.getImageInfo({ src: img }))).then(
imageInfos => {
console.log('Images then>>>>>>', imageInfos);
this.shareBg = imageInfos[0][1].path;
this.shareQrImageUrl = imageInfos[1][1].path;
this.avater = imageInfos[2][1].path;
//根据参数开始绘图
this.createdCanvas().then(()=>{
// 绘制需要延迟,不然第一次加载为空白
setTimeout(() => {
wx.canvasToTempFilePath({
x: 0,
y: 0,
canvasId: "mc",
success: res => {
_this.imgAddRess = res.tempFilePath;
console.log('真实图片>>>>>>', res);
},
fail:(error)=>{
console.log('error',error)
uni.showToast({
title: error.errMsg,
icon:'none',
duration:2000,
});
}
},this);
}, 500);
})
}
);
},
// 海报绘图
createdCanvas() {
const _this = this;
const query = wx.createSelectorQuery().in(this);
query.select('#mc').boundingClientRect(data => {
console.log("得到布局位置信息" + JSON.stringify(data));
}).exec();
return new Promise((resolve, reject) => {
//绘图上下文
const ctx = wx.createCanvasContext('mc', this);
wx.createSelectorQuery()
.in(this)
.select('#mc')
.boundingClientRect(rect => {
console.log('rect111',rect)
if(!rect) return
var width = rect.width;
var height = rect.height;
let qrw = (width-200) / 2;
let qrh = ((height - 200) / 2 + 80);
// 背景图
ctx.drawImage( _this.shareBg, 0, 0, width, height);
// 头像
ctx.drawImage(_this.avater, (width - 80) / 2 , 50 , 80, 80 );
// 文案
ctx.setFillStyle('#ffffff');
ctx.setTextAlign('center');
ctx.font = "bold 26px 'PingFangSC-Medium, PingFang SC'"
ctx.fillText('超能小飞侠', 180, 170, width);
ctx.font = "bold 16px 'DINAlternate-Bold, DINAlternate'"
ctx.setFillStyle('#000000');
ctx.setTextAlign('center');
ctx.fillText("诚邀您参与超能封神", 185, 205, width); // (文本,X坐标,Y坐标, 最大宽度)
// 二维码:(图片,X坐标,Y坐标, 图宽,图高)
ctx.drawImage(_this.shareQrImageUrl, qrw , qrh , 200, 200 );
//二维码图片
ctx.draw();
}).exec()
//获取节点
resolve()
})
},
检测版本与自行安装
<view @click="handleUpdate">检测新版本</view>
const handleUpdate = () => {
try {
// 获取当前应用版本
const currentVersion = plus.runtime.version;
console.log("获取当前应用版本: ", currentVersion)
// 请求服务器,获取最新版本信息
const res = await uni.request({
url: 'https://localhost:5566/check-update', // 查询最新版本接口
method: 'GET',
data: {
version: currentVersion
}
});
// const serverData = res.data;
// 假设服务器返回
const serverData = {
update: true,
latest: '1.1.0',
fileName: "check_"+dayjs().format("YYYYMMDDHHmmss")+".apk",
url: 'https://app.liuyingyong.cn/build/download/8bef5430-8d5b-11f0-a322-2597c8f390f2',
note: '测试自行安装', force: false }
if (serverData.update && serverData.latest > currentVersion) {
showUpdateDialog(serverData); // 显示更新弹窗
}
} catch (error) {
console.error('检查更新失败:', error);
}
}
function showUpdateDialog(updateInfo) {
uni.showModal({
title: `发现新版本 ${updateInfo.latest}`,
content: updateInfo.note || '本次更新带来了全新的功能和体验',
confirmText: '立即更新',
cancelText: updateInfo.force ? null : '稍后再说', // 强制更新时可能不显示取消按钮
success: (res) => {
if (res.confirm) {
downloadApk(updateInfo.url); // 开始下载APK
} else if (updateInfo.force) {
// 如果是强制更新,用户取消则退出应用
plus.runtime.quit();
}
}
});
}
function downloadApk(apkUrl) {
uni.showLoading({
title: '准备下载...',
mask: true
});
// 创建下载任务
const downloadTask = plus.downloader.createDownload(
apkUrl, {
filename: '_doc/download/check_new.apk' // 文件保存路径,_doc 目录可读写
},
(download, status) => {
console.log("打印download:", download, status)
uni.hideLoading();
if (status === 200) {
uni.showToast({
title: '下载完成,开始安装',
icon: 'none'
});
installApk(download.filename); // 下载完成,开始安装
} else {
uni.showToast({
title: '下载失败,请重试',
icon: 'none'
});
console.error('Download failed, status:', status);
}
}
);
// 监听下载进度变化
downloadTask.addEventListener('statechanged', (task, status) => {
if (task.downloadedSize && task.totalSize) {
const percent = (task.downloadedSize / task.totalSize * 100).toFixed(0);
uni.showLoading({
title: `下载中 ${percent}%`,
mask: true
}); // 或更新自定义进度条组件
}
});
downloadTask.start(); // 启动下载任务
}
function installApk(filePath) {
plus.runtime.install(
filePath, {
force: false // 是否强制安装
},
function() {
console.log('安装成功,重启应用');
plus.runtime.restart(); // 安装成功,重启应用
},
function(e) {
uni.showToast({
title: '安装失败',
icon: 'none'
});
console.error('安装失败:', e);
}
);
}
浙公网安备 33010602011771号