canvas
我一直都是只是了解状态,只是知道大概要怎么画,没有具体的项目实战经验,最近有个需求就是要用canvas画个分享的海报。看起来也不难,实际画起来还是花了不少实际,也踩了一些小坑,所以记录一下fillText
写文字fillStyle
用来设置字体颜色setFontSize
字号font
字符串,设置字体所有格式,字号和字体家族必填fillRect
画个长方形setFillStyle
填充颜色setStrokeStyle
设置画笔颜色moveTo
画笔起始点lineTo
画笔移动点stroke
绘制arc
画圆clip
裁剪drawImage
绘制图片draw
绘制我用到的所有方法都在上面了,本质上用画布作图可以理解为全部用css定位来布局,所已写起来很无脑。
我在使用drawImage
把图片绘制到canvas上的时候是正常的,但是当使用canvasToTempFilePath
导出成图片的时候就不正常了,里面的图片都没显示,其他文字线条都是正常的,这就很纳闷了,网上查了很多也没发现,后来观察代码猜想可能是由于是异步下载图片造成的,使用drawImage
步骤是这样的:
getImageInfo
获取图片的本地路径drawImage
绘制图片所以问题可能是getImageInfo
是异步操作,图片还没获取成功就开始导出了,还是要细心点
/**
* @description: 绘制分享海报
* @param {String} canvasId
* @param {Object} opt 绘制参数,包含:
* kuaidiComLogo
* kuaidiComName
* kuaidiNum
* tabIdName
*
* sendxzq
* sendcity
* sendaddr
* sendName
* sendmobile
*
* recxzq
* reccity
* recaddr
* recName
* recmobile
* @param {Number} type 海报样式 默认1
* @return {Promise} 图片url
*
* example:
* import { createShareCanvas } from'@/utils'
*
* this.shareImg = await createShareCanvas('shareImg', {...}, 2)
*/
export function createShareCanvas(canvasId, opt = {}, type = 1) {
return new Promise(async(resolve, reject) => {
if(!canvasId){
reject(new Error('请传入canvasId'))
}
const ctx = uni.createCanvasContext(canvasId)
// 顶部
ctx.setFillStyle('#317ee7')
ctx.fillRect(0, 0, 420, 70)
ctx.setFontSize(24)
ctx.fillStyle = '#fff'
if(opt.kuaidiNum) {
await new Promise((resolve, reject) => {
uni.getImageInfo({
src: opt.kuaidiComLogo,
success(img) {
console.log(img);
ctx.save()
ctx.beginPath()
ctx.arc(38, 33, 18, 0, 2 * Math.PI)
ctx.clip()
ctx.drawImage(img.path, 20, 15, 36, 36)
ctx.restore()
ctx.fillText(`${opt.kuaidiComName} ${opt.kuaidiNum}`, 70, 43)
ctx.draw(true)
resolve()
},
fail(err) {
reject(err)
}
});
})
} else {
ctx.fillStyle = '#98BEF3'
ctx.fillText('暂无快递单号', 20, 43)
}
// 中间信息,根据type展示不同样式
if(type === 1) {
console.log(ctx.measureText(opt.tabIdName))
ctx.fillText(opt.tabIdName, 338, 43)
// 收寄件人
ctx.setFillStyle('#f5f6f8')
ctx.fillRect(0, 70, 420, 224)
ctx.font = 'italic small-caps bold 12px/1'
ctx.fillStyle = '#333'
ctx.fillText(`${opt.sendName} ${opt.sendmobile}`, 75, 103.5)
// ctx.fillText(opt.sendmobile, 75 + ctx.measureText(opt.sendName).width + 15, 103.5)
ctx.fillText(`${opt.recName}`, 75, 195)
ctx.fillText(opt.recmobile, 75 + ctx.measureText(opt.recName).width + 15, 195)
// 地址
ctx.fillStyle = '#878787'
ctx.setFontSize(20)
let sendFullAddr = opt.sendxzq + opt.sendaddr
if(sendFullAddr.length <= 14) {
ctx.fillText(sendFullAddr, 75, 142)
} else {
ctx.fillText(sendFullAddr.substr(0, 14), 75, 131)
ctx.fillText(sendFullAddr.substr(14), 75, 159)
}
let recFullAddr = opt.recxzq + opt.recaddr
if(recFullAddr.length <= 14) {
ctx.fillText(recFullAddr, 75, 233)
} else {
ctx.fillText(recFullAddr.substr(0, 14), 75, 233)
ctx.fillText(recFullAddr.substr(14), 75, 261)
}
ctx.setStrokeStyle('#e2e2e2')
ctx.moveTo(75, 166)
ctx.lineTo(400, 166)
ctx.stroke()
const p1 = new Promise((resolve, reject) => {
uni.getImageInfo({
src: 'https://cdn.kuaidi100.com/images/wxapp/kuaidi100/ico-ji.png',
success(img) {
console.log(img);
ctx.drawImage(img.path, 20, 92, 28, 34)
// ctx.draw(true)
resolve()
},
fail(err) {
reject(err)
}
});
})
const p2 = new Promise((resolve, reject) => {
uni.getImageInfo({
src: 'https://cdn.kuaidi100.com/images/better/arrow_down.png',
success(img) {
console.log(img);
ctx.drawImage(img.path, 24, 135, 18, 37)
// ctx.draw(true)
resolve()
},
fail(err) {
reject(err)
}
});
})
const p3 = new Promise((resolve, reject) => {
uni.getImageInfo({
src: 'https://cdn.kuaidi100.com/images/wxapp/kuaidi100/ico-shou.png',
success(img) {
console.log(img);
ctx.drawImage(img.path, 20, 183, 28, 34)
// ctx.draw(true)
resolve()
},
fail(err) {
reject(err)
}
});
})
await Promise.all([p1,p2,p3]).catch(err => console.log(err))
} else if(type === 2) {
ctx.fillStyle = '#333'
ctx.setFontSize(30)
ctx.fillText(opt.sendcity, 105-ctx.measureText(opt.sendcity).width/2, 165)
ctx.fillText(opt.reccity, 315 - ctx.measureText(opt.reccity).width/2, 165)
ctx.setStrokeStyle('#e2e2e2')
ctx.moveTo(173, 155)
ctx.lineTo(193, 155)
ctx.stroke()
ctx.moveTo(228, 155)
ctx.lineTo(248, 155)
ctx.stroke()
ctx.fillStyle = '#878787'
ctx.setFontSize(24)
ctx.fillText(opt.sendName, 105-ctx.measureText(opt.sendName).width/2, 215)
ctx.fillText(opt.recName, 315 - ctx.measureText(opt.recName).width/2, 215)
ctx.fillStyle = '#317EE7'
ctx.setFontSize(24)
ctx.fillText(opt.tabIdName, 210-ctx.measureText(opt.tabIdName).width/2, 215)
await new Promise((resolve, reject) => {
uni.getImageInfo({
src: 'https://cdn.kuaidi100.com/images/wxapp/kuaidi100/plane.png',
success(img) {
console.log(img);
ctx.drawImage(img.path, 198, 143,24, 22, )
ctx.draw(true)
resolve()
},
fail(err) {
reject(err)
}
});
})
}
// 底部时间
ctx.fillStyle = '#fff'
ctx.fillRect(0, 274, 420, 43 )
ctx.fillStyle = '#333'
ctx.fillText(`寄出时间:${opt.lastupDate}`, 20, 304)
setTimeout(() => {
ctx.draw(true, () => {
uni.canvasToTempFilePath({
x: 0,
y: 0,
width: 420,
height: 337,
destWidth: 420,
destHeight: 337,
canvasId,
success: function(res) {
// 在H5平台下,tempFilePath 为 base64
console.log(res.tempFilePath)
resolve(res.tempFilePath)
} ,
fail(err) {
console.log(err)
reject(err)
}
})
})
}, 50)
})
}