最近做了一个需求,一个推广活动,在小程序中生成推广海报并且支持保存在手机相册,UI稿中呈现的是一个非全屏的固定宽高的海报图片,在保存的时候遇到了如下问题并一一解决
【问题1】开发者工具中可以正常绘制海报,真机上却不行
【解决】因为绘制的海报中包含banner和太阳码图片,需要使用内置方法.getImageInfo()获取解析一下图片信息,才能绘制在画布当中
【问题2】真机中偶现canvas画布中文字绘制不全
【解决】context.draw()这个行为是异步的,我们使用uni.canvasToTempFilePath的行为进行海报的绘制,使用settimeout处理异步即可
context.draw(false, () => {
setTimeout(() => {
uni.canvasToTempFilePath({
canvasId: "posterCanvas",
destWidth: 558 * 2,
destHeight: 770 * 2,
success(res) {
_this.posterImgSrc = res.tempFilePath;
}
});
}, 100);
});
【问题3】生成的海报没有背景色,是透明的
【解决】之前使用的填充方式是context.setFillStyle("rgba(255, 255, 255, 255)");context.fillRect(0, 0, 279 * rpx, 385 * rpx);设置背景色,canvas生成海报之后导出,背景色始终为透明的,后来参考了网上的方案,设置context.fillStyle = "#fff";context.fillRect(0, 0, 279 * rpx, 385 * rpx);之后成功实现了带白色背景色导出,setFillStyle和直接设置属性有什么差别我一开始只是解决问题没有搞明白,后来多方查询,找到了原因如下:fillStyle 属性设置或返回用于填充绘画的颜色、渐变或模式。setFillStyle是用于设置填充色的方法,为什么在绘制海报的时候setFillStyle这个方法没有生效,在微信的官方api中终于找到了答案
CanvasContext.setFillStyle(string|CanvasGradient color)
从基础库 1.9.90 开始,本接口停止维护,请使用 CanvasContext.fillStyle 代替
设置填充色。
嗯,所以fillStyle生效了,哈哈哈
【问题4】多种移动端生成的海报显示有偏差,没有达到理想的效果
【解决】获取设备宽高,计算和标准设计稿的偏差,然后在绘制时,数值均用此偏差进行处理
//获取不同设备的宽高适配
let { windowWidth, windowHeight } = systemInfo;
let rpx = 1;
//个性化定义针对不同设备的单位比率,和标准设计稿375做比
rpx = windowWidth / 375;
【完整代码如下,绘制海报的方法!】生成海报(使用canvas绘制海报banner+中间提示文字+底部太阳码,简单的太阳码分享海报)的源码(注:我们的小程序是uniapp的开发的,所以wx. 均为uni.)
drawCanvas() {
//创建画布区域
let context = uni.createCanvasContext("posterCanvas");
//记录当前this
let _this = this;
//获取屏幕信息;
let systemInfo = new Promise((res, rej) => {
uni.getSystemInfo({
success: data => {
res(data);
}
});
});
systemInfo.then(systemInfo => {
//获取不同设备的宽高适配
let { windowWidth, windowHeight } = systemInfo;
let rpx = 1;
//个性化定义针对不同设备的单位比率,和标准设计稿375做比
rpx = windowWidth / 375;
//此处279和385为我的设计稿当中的海报宽高
context.clearRect(0, 0, 279 * rpx, 385 * rpx);
//设置海报背景为白色
context.fillStyle = "#fff";
//填充背景颜色
context.fillRect(0, 0, 279 * rpx, 385 * rpx);
let path = this.posterBanner;//我的posterbanner是一个固定的图片链接
let imagePromise = [img1, img2];
imagePromise = imagePromise.map(src => {
return new Promise((res, rej) => {
//不获取一下图片信息会导致图片在真机上绘制失败
uni.getImageInfo({
src,
success: data => {
console.log("imgdata--->", data);
res(data);
},
fail(err) {
console.log("err", err);
}
});
});
});
Promise.all(imagePromise).then(imgsInfo => {
context.drawImage(
imgsInfo[0].path,
12.5 * rpx,
12.5 * rpx,
254 * rpx,
173 * rpx
);
context.setFontSize(14 * rpx);
context.setTextAlign("center");
context.setFillStyle("#390C59");
context.fillText(
"xxxxxxxxxxxx一段文字",
(279 * rpx) / 2,
205.5 * rpx
);
context.setFontSize(14 * rpx);
context.setTextAlign("center");
context.setFillStyle("#390C59");
context.fillText("xxxx一段文字!", (279 * rpx) / 2, 226.5 * rpx);
context.drawImage(
imgsInfo[1].path,
102 * rpx,
262.5 * rpx,
75 * rpx,
75 * rpx
);
context.setFontSize(12 * rpx);
context.setTextAlign("center");
context.setFillStyle("#390C59");
context.fillText("长按扫码查看详情", (279 * rpx) / 2, 360.5 * rpx);
context.draw(false, () => {
//因为draw是异步的,移动端偶发绘制文字丢失,所以增加settimeout
setTimeout(() => {
uni.canvasToTempFilePath({
canvasId: "posterCanvas",
destWidth: 558 * 2,
destHeight: 770 * 2,
success(res) {
//拿到生成的画布图片临时路径在res.tempFilePath中,可以自行存储或者赋值
}
});
}, 100);
});
});
});
},