准备
准备两张图片连接,最好是自己开发账号验证的https图片链接。
实现思路
其实就是canvas实现方式,首先要就是定义一个canvas容器,把容器放在中间,图片也要动态计算大小居中,显示下面的文字和二维码也是要根据容器动态去改变。
实现代码
- 下载头像,将图片缓存到本地
async onLoad() {
let url1 = 'https://static-client-image.jrucker.cn/image/1547303314501.jpeg';
let url2 = 'https://static-client-image.jrucker.cn/image/1550757251583.jpeg';
this.getAsyncPic(url1, url2);
},
async getAsyncPic(url1, url2) {
let res = await this.getUrlPromise(url1, url2);
if (res.length && res.length === 2) {
let [bannerObj, wxcodePicObj] = res;
this.setData({
'canvas.banner': bannerObj.banner,
'canvas.wxcodePic': wxcodePicObj.wxcodePic
});
}
},
async getUrlPromise(url1, url2) {
// 生成海报的封面图缓存到本地
const promise1 = new Promise((resolve, reject) => {
wx.getImageInfo({ // 小程序获取图片信息API
src: url1,
success: function (res) {
resolve({
banner: res.path
})
},
fail(err) {
reject(err)
}
})
});
// 生成海报的小程序图缓存到本地
const promise2 = new Promise((resolve, reject) => {
wx.getImageInfo({ // 小程序获取图片信息API
src: url2,
success: function (res) {
resolve({
wxcodePic: res.path
})
},
fail(err) {
reject(err)
}
})
});
let res = await Promise.all([promise1, promise2]);
const promise = new Promise((resolve) => {
resolve(res ? res : [])
});
return promise;
},
- 绘制海报
async setPoster() {
if (this.data.posterImage) {
return this.setData({
visible: true,
posterImage: this.data.posterImage
})
}
wx.showLoading({title: '海报生成中...'});
if (!this.data.canvas.banner || !this.data.canvas.wxcodePic) {
let url1 = 'https://static-client-image.jrucker.cn/image/1547303314501.jpeg';
let url2 = 'https://static-client-image.jrucker.cn/image/1550757251583.jpeg';
await this.getAsyncPic(url1, url2);
}
const ctx = wx.createCanvasContext('canvas-box');
ctx.setFillStyle('#ffffff');
ctx.fillRect(0, 0, 270, 330);
// 绘制顶部banner
ctx.drawImage(this.data.canvas.banner, 0, 0, 270, 170);
this.dealWords({
ctx: ctx, // 画布上下文
fontSize: 16, // 字体大小
word: this.data.canvas.title, // 需要处理的文字
maxWidth: 255, // 一行文字最大宽度
fillStyle: '#333333',
x: 8, // 文字在x轴要显示的位置
y: 177, // 文字在y轴要显示的位置
maxLine: 1 // 文字最多显示的行数
});
this.dealWords({
ctx: ctx,
fontSize: 14,
word: this.data.canvas.desc,
maxWidth: 260,
fillStyle: '#999999',
x: 8,
y: 201,
maxLine: 2
});
ctx.moveTo(0, 252);
ctx.lineTo(270, 252);
ctx.setLineWidth(0.5);
ctx.setStrokeStyle('#f2f2f2');
ctx.stroke();
// 绘制小程序码
ctx.drawImage(this.data.canvas.wxcodePic, 9, 261, 60, 60);
ctx.setFontSize(13);
ctx.setFillStyle('#999999');
ctx.fillText('长按扫码查看详情', 80, 296);
ctx.draw();
setTimeout(() => {
wx.canvasToTempFilePath({
quality: 1,
canvasId: 'canvas-box',
success: (res) => {
let tempFilePath = res.tempFilePath;
wx.hideLoading();
this.setData({
visible: true,
posterImage: tempFilePath
})
},
fail: (res) => {
console.log(res);
}
});
}, 1000)
}
- 绘制多行文本
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;//每一行当前宽度
options.ctx.setFillStyle(options.fillStyle);
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) * 18); //(j+1)*18这是每一行的高度
} else {
options.ctx.fillText(nowStr.slice(0, m), options.x, options.y + (j + 1) * 18);
}
endPos += m;//下次截断点
break;
}
}
} else {//如果当前的字符串宽度小于最大宽度就直接输出
options.ctx.fillText(nowStr.slice(0), options.x, options.y + (j + 1) * 18);
}
}
}
- 保存绘制图片至相册,需提前判断是否已授权,这一步就是最后把canvas生成图片了,大功告成。
savePoster() {
const canvasToTempFilePath = () => {
wx.showLoading({title: '加载中'});
wx.canvasToTempFilePath({
quality: 1,
canvasId: 'canvas-box',
fileType: 'png',
success: (res) => {
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: (res) => {
wx.hideLoading();
wx.showToast({
title: '保存成功',
});
this.setData({
visible: false
})
},
fail() {
wx.hideLoading()
}
})
}
})
};
/*查看是否授权*/
wx.getSetting({
success: (res) => {
if (!res.authSetting['scope.writePhotosAlbum']) {
wx.authorize({
scope: 'scope.writePhotosAlbum',
success() { //这里是用户同意授权后的回调
canvasToTempFilePath();
},
fail() { //这里是用户拒绝授权后的回调
wx.showModal({
title: '提示',
content: '若不打开授权,则无法将图片保存在相册中!',
showCancel: true,
cancelText: '暂不授权',
cancelColor: '#000000',
confirmText: '去授权',
confirmColor: '#3CC51F',
success: function (res) {
if (res.confirm) {
wx.openSetting({
//调起客户端小程序设置界面,返回用户设置的操作结果。
})
} else {
console.log('用户点击取消')
}
}
})
}
})
} else {
canvasToTempFilePath()
}
}
});
}
注意
1、图片要提前下载
这里面还有一个问题就是,图片要提前下载完之后再绘图,不然图片显示不出来,可以把下载图片的方法单独拎出来,然后下载图片后回调绘图方法。
2、ctx.draw()
这个方法是在绘制完成之后在调用,不然容易其它被覆盖。