背景
最近项目中有一个下载图片和一个分享海报的功能,遇到了一些问题,比如海报图片生成失败、图片下载是空白的、多张图片下载时下载的还是前一张图片等等。觉得过程挺有意思的,记录一下。
创建容器及样式
<view class="canvas-container">
<canvas
:style="{ width: canvasW + 'px', height: canvasH + 'px' }"
canvas-id="myCanvas"
id="myCanvas01"
>
</canvas>
</view>
<view class="convas-image">
<canvas :style="{ width: canvasW + 'px',height: canvasH + 'px'}" canvasid="myCanvas2" id="myCanvas02">
</canvas>
</view>
由于要做移动端适配,故容器的宽高是动态的。
.canvas-container {
position: fixed;
top: 9999999px;
#myCanvas01 {
background: rgba(255, 255, 255, 0); /*关键点*/
}
}
.convas-image {
position: fixed;
top: 9999999px;
#myCanvas02 {
background: rgba(255, 255, 255, 0); /*关键点*/
}
}
因为这两个容器实际上都是不显示在页面上的,如果使用hidden或者v-if隐藏的话多多少少都会有些问题,所以使用固定定位使容器脱离文档流。
准备工作
// 获取设备信息
getSystemInfo() {
return new Promise((req, rej) => {
uni.getSystemInfo({
success: function (res) {
req(res);
},
});
});
},
getImageInfo(image) {
console.log(`output->image`, image);
return new Promise((req, rej) => {
uni.getImageInfo({
src: image,
success: function (res) {
req(res);
},
fail: (err) => {
uni.showToast({
icon: "error",
mask: true,
title: "获取照片失败",
});
console.log("****", err);
},
});
});
},
获取设备信息及辅助函数,因为canvas中使用的图片为网络方式返回,此函数确保图片被正常读取后再绘制,后文有用到。
海报的绘制
async canvas2dFun(retryCount = 3) {
this.SystemInfo = await this.getSystemInfo();
const w = this.SystemInfo.windowWidth / 750;
let goodsImgPath = await this.getImageInfo(this.imgObj.urls[0]);//海报主图
let ewmImgPath = await this.getImageInfo(this.imgObj.qrCode);//海报二维码
let posterPath = await this.getImageInfo(
`${this.imgpath}/static_pro/create/poster.png`
);//海报边框
let logoPath = await this.getImageInfo(
`${this.imgpath}/static_pro/create/logo.png`
);//logo
this.canvasW = 710 * w;
this.canvasH = 1124 * w;
if (this.SystemInfo.errMsg == "getSystemInfo:ok") {
console.log("读取图片信息成功");
var ctx = uni.createCanvasContext("myCanvas", this);
// 1.填充背景色,白色
// 设置画布透明度
ctx.setFillStyle("rgba(255, 255, 255, 1)"); // 默认白色
ctx.fillRect(0, 0, this.canvasW, this.canvasH); // fillRect(x,y,宽度,高度)
ctx.drawImage(
goodsImgPath.path,
5 * w,
5 * w,
this.canvasW - 15 * w,
this.canvasH - 15 * w
); // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度)
ctx.drawImage(posterPath.path, 0, 0, this.canvasW, this.canvasH); // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度,二维码的宽,高)
ctx.drawImage(
logoPath.path,
this.canvasW - this.ewmW * w - 460 * w,
this.canvasH - this.ewmW * w - 100 * w,
this.logoW * w - 30 * w,
this.logoH * w - 30 * w
); //logo
ctx.drawImage(
ewmImgPath.path,
this.canvasW - this.ewmW * w - 460 * w,
this.canvasH - this.ewmW * w - 16 * w,
this.ewmW * w - 30 * w,
this.ewmW * w - 30 * w
); // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度,二维码的宽,高)
ctx.font = "bold 18px Arial";
ctx.fillStyle = "#222222";
ctx.fillText(
"微信扫码 XX相机",
this.canvasW - this.ewmW * w - 280 * w,
this.canvasH - this.ewmW * w + 50 * w
);
ctx.font = "normal 12px Arial";
ctx.fillText(
"这是我的写真,你也来试一试吧",
this.canvasW - this.ewmW * w - 280 * w,
this.canvasH - this.ewmW * w + 100 * w
);
// draw方法 把以上内容画到 canvas 中
ctx.draw(true, (ret) => {
uni.canvasToTempFilePath(
{
// 保存canvas为图片
canvasId: "myCanvas",
quality: 1,
success: (res) => {
console.log("生成海报-》", res);
uni.setStorageSync("filePath", res.tempFilePath); // 保存临时文件路径到缓存
},
fail: (error) => {
console.log("生成海报失败-》", error);
if (retryCount > 0) {
console.log(`重试剩余次数: ${retryCount - 1}`);
this.canvas2dFun(retryCount - 1)
}
},
},
this
);
});
} else {
console.log("读取图片信息失败");
}
},
绘制完成之后将绘制的图片存入filePath临时路径中,方便后面下载等操作。
测试阶段发现,部分安卓机型在canvas第一次绘制时会报wx.canvasToTempFilePath:create bitmap failed,可能是不分机型的性能使得图形绘制失败,图片保存下来也是纯白色的底图。在这里fail回调处理了如果生成失败再生成一次的操作。
shareFirendCircle() {
let posterUrl = uni.getStorageSync("filePath");
uni.showShareImageMenu({
path: posterUrl, //图片地址必须为本地路径或者临时路径
success: (re) => {
console.log({ success: re });
},
fail: (re) => {
console.log({ fail: re });
},
});
},
页面按钮点击后调用函数唤起分享框,函数中的posterUrl为canvas绘制的图片。

图片的下载
async canvas2dImage(retryCount = 3) {
this.SystemInfo = await this.getSystemInfo();
const w = this.SystemInfo.windowWidth / 750;
let goodsImgPath = await this.getImageInfo(this.imgObj.urls[this.swiperIndex]);
let logoPath = await this.getImageInfo(
`${this.imgpath}/static_pro/create/logo.png`
);
this.canvasW = 710 * w;
this.canvasH = 1124 * w;
if (this.SystemInfo.errMsg == "getSystemInfo:ok") {
console.log("读取图片信息成功");
var ctx = uni.createCanvasContext("myCanvas2", this);
// 1.填充背景色,白色
// 设置画布透明度
ctx.setFillStyle("rgba(255, 255, 255, 1)"); // 默认白色
ctx.fillRect(0, 0, this.canvasW, this.canvasH); // fillRect(x,y,宽度,高度)
ctx.drawImage(
goodsImgPath.path,
5 * w,
5 * w,
this.canvasW - 15 * w,
this.canvasH - 15 * w
); // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度)
ctx.drawImage(
logoPath.path,
this.canvasW - this.ewmW * w - 480 * w,
this.canvasH - this.ewmW * w + 100 * w,
this.logoW * w - 30 * w,
this.logoH * w - 30 * w
); //logo
// draw方法 把以上内容画到 canvas 中
return new Promise((resolve, reject) => {
ctx.draw(false, (ret) => {
uni.canvasToTempFilePath(
{
// 保存canvas为图片
canvasId: "myCanvas2",
quality: 1,
success: (res) => {
console.log("生成高清图-》", res);
uni.setStorageSync("ImagePath", res.tempFilePath); // 保存临时文件路径到缓存
resolve();
},
fail: (error) => {
console.log("生成高清图失败-》", error);
if (retryCount > 0) {
console.log(`重试剩余次数: ${retryCount - 1}`);
this.canvas2dImage(retryCount - 1)
.then(resolve)
.catch(reject);
} else {
reject(error);
}
},
},
this
);
});
});
} else {
console.log("读取图片信息失败");
}
},
这里使用Promise的原因是有多张图片需要单独下载,切换图片点击下载按钮开始绘制图像,确保在图像canvas生成完毕后继续下载的步骤。
canvas绘制方面与上面大差不差,主要是下载方面需要单独配置
async dowloadImg() {
if (!this.$utils.getUserInfo().isVip) {
//无会员
this.openPlusAlert = true;
} else {
uni.showLoading({
title: "照片保存中",
});
await this.canvas2dImage();
let that = this;
// 获取用户是否开启 授权保存图片到相册。
uni.getSetting({
success(res) {
console.log("已知权限", res);
uni.hideLoading();
// 如果没有授权
if (!res.authSetting["scope.writePhotosAlbum"]) {
// 则拉起授权窗口
uni.authorize({
scope: "scope.writePhotosAlbum",
success() {
that.saveImage();
},
fail(error) {
//点击了拒绝授权后--就一直会进入失败回调函数--此时就可以在这里重新拉起授权窗口
console.log("拒绝授权则拉起弹框", error);
uni.showModal({
title: "提示",
content: "若点击不授权,将无法保存图片",
cancelText: "不授权",
cancelColor: "#999",
confirmText: "授权",
confirmColor: "#f94218",
success(res) {
console.log(res);
if (res.confirm) {
// 选择弹框内授权
uni.openSetting({
success(res) {
console.log(res.authSetting);
},
});
} else if (res.cancel) {
// 选择弹框内 不授权
console.log("用户点击不授权");
}
},
});
},
});
} else {
// 有权限--直接保存
console.log("有权限 直接调用相应方法");
uni.hideLoading();
that.saveImage();
}
},
fail: (error) => {
console.log(
"调用微信的查取权限接口失败,并不知道有无权限!只有success调用成功才只知道有无权限",
error
);
uni.hideLoading();
uni.showToast({
title: error.errMsg,
icon: "none",
duration: 1500,
});
},
});
}
},
这里是点击下载绑定的函数,主要作用是判断是否开启了权限。
saveImage() {
let filePath = uni.getStorageSync("ImagePath"); //从缓存中读取临时文件路径
wx.saveImageToPhotosAlbum({
filePath: filePath,
success(res) {
uni.showToast({
icon: "success",
mask: true,
title: "保存到相册了",
});
},
fail(res) {
console.log(res.errMsg);
},
});
},
保存图片到本地的操作。中心思想还是把canvas生成的临时路径下载保存到本地。
小结
这样就完成了上面轮播图切换时下载对应的图片,以及海报的分享与下载功能。
1万+

被折叠的 条评论
为什么被折叠?



