这次的业务逻辑中要用到小程序canvas。
这个canvas的要求是一段文字中间会加入一张图片然后在接下去写一段文字内容。并且文字和图片是随机出现的,因此只能用for循环来做。这里有一个问题就是canvas绘制文字的速度比绘制图片的速度快很多,一个for循环之后,for循环中的文字绘制已经执行完了但是,for循环中的图片还没有绘制好,就已经执行了for循环外面的context.draw()方法绘制好了canvas。这样图片就没有绘制上去。
解决思路:等待每一个for循环中的无论文字还是图片绘制完成之后在执行下一个for循环。
这里我用的是es6的async和await
代码:
async drawCanvas () {
wx.showLoading({
title: '加载中',
})
const context = wx.createCanvasContext('Canvas')
const w = (wx.getSystemInfoSync().screenWidth / 375)//设备宽度比例
let that = this
let row = ''//每行文字
let H = w * (250/that.data.imgWidth) * that.data.imgHeight//canvas主图高
let startHeight = 0//开始画的高度
let h = 0
context.setFillStyle('#ffffff')//画背景色
context.fillRect(0, 0, 500, 816)//画背景色
//引用本地图片
context.drawImage(that.data.img,0,0,w * 250,H)//引入图片context.drawImage('路径','X轴距离','Y轴距离','引入图片长','引入图片宽')
startHeight = H + 11
context.setFontSize(w * 11);//设置文字大小
context.setFillStyle("#333333");//设置文字颜色
context.fillText(this.data.title,w * 13, w * (H + 12));//文字内容和坐标位置
for (let j = 0;j < that.data.content.detail.length;j++) {
if (that.data.content.detail[j].type == 1) {//画文字
let pItem = that.data.content.detail[j].content.word.split('')
for (let i = 0;i < pItem.length;i++) {
row += pItem[i]
if (context.measureText(row).width >= w * 224) {//拼接每一行的字符串
if (w * (18 + startHeight) >= 390) {//当篇幅太大所能支持的最大行数
row = row.substring(0,5) + '...'
await context.setFontSize(w * 9);
await context.setFillStyle("#333333");
await context.fillText(row,w * 13,w * (18 + startHeight),w * 224);
startHeight += 15
break;
}
await context.setFontSize(w * 9);
await context.setFillStyle("#333333");
await context.fillText(row,w * 13, w * (18 + startHeight),w * 224)
startHeight += 15
row = ''
}
if (i == pItem.length - 1) {//最后一次且不到最大篇幅时画上去
await context.setFontSize(w * 9);
await context.setFillStyle("#333333");
await context.fillText(row,w * 13, w * (18 + startHeight),w * 224);
startHeight += 15
row = ''
}
}
}
else if (that.data.content.detail[j].type == 0) {//画图
if (startHeight > 390) {
return
}
let img = that.data.content.detail[j].content.img
await new Promise(function (resolve,reject) {
wx.getImageInfo({
src: img,
success: async res => {
h = (224/res.width) * res.height
resolve()
}
})
}).then(async function () {
await context.drawImage(img,w * 13,w * (startHeight + 10),w * 224,w * h)
startHeight = startHeight + h + 5
})
}
}
context.draw()//生成canvas图片
that.setData({
isCanvas : true
})
wx.hideLoading()
},
其中绘制图片的时候需要回去到每一张图片的高度和宽度信息来进行等比缩放这里,这里就用到了wx的wx.getImageInfo()这个api,这里的成功回调是个传入的回调函数,await不起效果。这里我直接实例化一个promise将成功回调函数中的操作方到了then中。
最终效果:
总结:for循环中默认不会等待前一个循环中的所有程序都运行结束才去下一个for循环,而是直接不等待循环中的异步操作结束就像进入下一个循环。那么遇到for循环中后一个循环依赖前一个循环的结果这种情况async和await是一个很简便的处理方式。