需求背景
小程序不能直接分享至朋友圈,所以采用海报(最关键的就是邀请码)邀请好友。 从展示层面来讲,就是几张图,通过定位放在一起即可,但是关键是要保存,这个时候,就需要canvas去进行图片合成。那么canvas绘制海报,就拆分两个部分
通过css ,给用户展现的组合图片,这跟简单
通过css 布局,将各个元素组合在一起。这个想必不用多说,
保存时,通过canvas绘图,将图片合成一个完整的图片
canvas 绘图,有两种模式
-
新版 2d 模式(有坑,慎用) -
老版 canvas-id=“drawPoster”,虽然操作繁琐,但是兼容性好 获取图片尺寸
downShareImage(imgurl) {
console.log('imgurl', imgurl);
return new Promise((resolve, reject) => {
wx.getImageInfo({
src: imgurl,
success: (res) => {
let { width, height, path } = res;
resolve({
width,
height,
path,
});
},
fail: () => {
wx.showToast({
title: '下载失败',
icon: 'none',
});
reject('下载失败...');
},
});
});
},
canvas 绘图
async drawPoster() {
const { qrCodeImg, avatarImg, downImgRes, nickname } = this.data;
const ctx = wx.createCanvasContext('drawPoster');
ctx.drawImage(downImgRes.path, 0, 0, downImgRes.width, downImgRes.height);
const avatarImgW = 233;
const qrCodeW = 220;
const radius = 233 / 2;
let qrRadious = qrCodeW / 2;
let avX = downImgRes.width / 2 - avatarImgW + 120;
let avY = 100;
let qrX = downImgRes.width / 2 - qrCodeW + 110;
let qrY = 990;
ctx.save();
ctx.beginPath();
ctx.arc(avX + radius, avY + radius, radius, 0, 2 * Math.PI);
ctx.clip();
ctx.drawImage(avatarImg, avX, avY, 233, 233);
ctx.restore();
ctx.setFontSize(50);
ctx.setTextAlign('center');
ctx.setFillStyle('#7700d9');
ctx.fillText(nickname, downImgRes.width / 2, 415);
ctx.save();
ctx.arc(qrX + qrRadious, qrY + qrRadious, qrRadious, 0, 2 * Math.PI);
ctx.clip();
ctx.drawImage(qrCodeImg, qrX, qrY, 220, 220);
ctx.restore();
ctx.draw(false, async () => {
let canvasData = await this.exportPoster();
this.setData({
canvasData,
});
});
},
canvas合成图片
exportPoster() {
return new Promise((resolve, reject) => {
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: 1061,
height: 1404,
canvasId: 'drawPoster',
fileType: 'png',
success(res) {
resolve(res.tempFilePath);
},
fail() {
wx.showToast({
title: '下载失败',
icon: 'none',
});
reject('二维码生成失败');
},
});
});
},
长按保存:
saveImage() {
const { canvasData } = this.data;
if (!canvasData)
return wx.showToast({
title: '海报生成中',
icon: 'none',
});
if (this.drawPosterLock) return;
this.drawPosterLock = true;
wx.saveImageToPhotosAlbum({
filePath: canvasData,
success: (res) => {
if (res.errMsg == 'saveImageToPhotosAlbum:ok') {
wx.showToast({
title: '保存成功',
});
}
},
complete: () => {
this.drawPosterLock = false;
},
fail: () => {
wx.showToast({
title: '保存失败',
icon: 'none',
});
},
});
},
授权逻辑
handlerSavePhoto() {
let touchTime = this.data.touchEnd - this.data.touchStart;
console.log('touchTime', touchTime);
if (touchTime > 800) {
wx.getSetting({
success: (res) => {
var userPhotosAlbum = res.authSetting['scope.writePhotosAlbum'];
if (userPhotosAlbum) {
this.saveImage();
return;
}
if (userPhotosAlbum == undefined) {
wx.authorize({
scope: 'scope.writePhotosAlbum',
success: (res) => {
if (res.errMsg == 'authorize:ok') {
this.saveImage();
}
},
});
return;
}
if (userPhotosAlbum != 'undefined') {
wx.showModal({
title: '提示',
content: '暂无权限,请开启使用权限',
success: (res) => {
if (res.confirm) {
wx.openSetting({
success: (res) => {
if (res.authSetting['scope.writePhotosAlbum']) {
this.saveImage();
} else {
}
},
});
}
},
});
}
},
});
}
},
总结:
- 我们在画canvas总是会担心尺寸问题,并且由尺寸带来的绘图模糊问题。因为小程序单位是rpx(根据不同机型进行适配的单位),但是canvas绘图单位是px,这里有一个好方法。
- 其实给用户看的,就是一个css布局的图片,用小程序本身的单位,就能解决不同分辨率的问题,而且渲染速度特别快。
- 用户保存的时候,保存的是canvas画图,合成图片的照片,肯定是px,那么你给canvas宽高,只要和目标图一致即可,或者放大两倍,都是ok的
- 由于画图需要拿到数据,并且是异步的,要用await去做同步顺序
- 利用promise封装很优美
- 用ctx.clip();去裁剪图片,会破坏当前的画布环境 ,所以需要先保存之前的画布环境,然后开始画,画完恢复之前的画布环境
|