小程序Canvas踩坑实录
<!-- index.wxml -->
<canvas
id="domo-canvas"
type="2d"
style="width: 375px; height: 600px;"
></canvas>
Canvas绘制函数
-
背景填充
toFillBgColor (ctx) { ctx.fillStyle = '#999999'; ctx.fillRect(0, 0, 375, 600); }, -
头像绘制
toPaintAvatar ( ctx, canvas, avatar_url ) { const avatarImg = canvas.createImage() avatarImg.src = avatar_url; avatarImg.onload = () => { var avatarurl_width = 85; //绘制的头像宽度 var avatarurl_heigth = 85; //绘制的头像高度 var avatarurl_x = 145; //绘制的头像与画布左边缘的距离 var avatarurl_y = 45; //绘制的头像与画布上边缘的距离 ctx.save(); ctx.beginPath(); ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math.PI *2, false); ctx.closePath() ctx.clip(); ctx.drawImage(avatarImg, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth); // 推进去图片,必须是https图片 console.log('toPaintAvatar') } }, -
海报绘制
toPaintPost ( ctx, canvas, post_url ) { const postImg = canvas.createImage() postImg.src = post_url; postImg.onload = () => { ctx.drawImage(postImg, 37.5, 225, 300, 300) console.log('toPaintPost') } }
仅 - 绘制头像时
/**
* @description 绘制 Canvas
*/
toCreateCanvas () {
let canvasId = `#domo-canvas`
const query = wx.createSelectorQuery()
query.select( canvasId )
.fields({ node: true, size: true, })
.exec(
(res) => {
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
canvas.width = res[0].width;
canvas.height = res[0].height;
this.toFillBgColor( ctx )
// 头像绘制
this.toPaintAvatar( ctx, canvas, this.data.avatar_url )
// 海报绘制
// this.toPaintPost ( ctx, canvas, this.data.post_url )
}
)
},
效果如下:
仅 - 绘制海报时
/**
* @description 绘制 Canvas
*/
toCreateCanvas () {
...
// 头像绘制
// this.toPaintAvatar( ctx, canvas, this.data.avatar_url )
// 海报绘制
this.toPaintPost ( ctx, canvas, this.data.post_url )
...
},
效果如下:
当「头像」和「海报」同时绘制时
/**
* @description 绘制 Canvas
*/
toCreateCanvas () {
...
// 头像绘制
this.toPaintAvatar( ctx, canvas, this.data.avatar_url )
// 海报绘制
this.toPaintPost ( ctx, canvas, this.data.post_url )
...
},
效果如下:
思考:为什么会出现图片绘制失败的情况?
猜测:因为执行顺序?
方案:我们来调换一下头像和海报的执行顺序试试。
/**
* @description 绘制 Canvas
*/
toCreateCanvas () {
...
// 海报绘制
this.toPaintPost ( ctx, canvas, this.data.post_url )
// 头像绘制
this.toPaintAvatar( ctx, canvas, this.data.avatar_url )
...
},
结果依然是两种情况都会出现。
思考:上述情况,「头像」都一定会被绘制出来。
猜测:头像的绘制,有概率,中断了海报的绘制。
方案:将头像的绘制中的「画圆」逻辑去掉试试。
/**
* @description 绘制 Canvas
*/
toCreateCanvas () {
...
// 头像绘制
this.toPaintAvatar( ctx, canvas, this.data.avatar_url )
// 海报绘制
this.toPaintPost ( ctx, canvas, this.data.post_url )
...
},
/**
* @description 头像绘制 (方形)
*/
toPaintAvatar ( ctx, canvas, avatar_url ) {
const avatarImg = canvas.createImage()
avatarImg.src = avatar_url;
avatarImg.onload = () => {
ctx.drawImage(avatarImg, 145, 45, 85, 85); // 推进去图片,必须是https图片
}
}
效果如下:
OHHHH!!!会100%成功绘制出预期内容
结论:绘制圆形图片会有几率导致其他图片的绘制失败。
猜想:是画圆时的操作阻断了后续的绘制
翻阅文档:
CanvasContext.clip()
功能描述
从原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。可以在使用
clip方法前通过使用save方法对当前画布区域进行保存,并在以后的任意时间通过restore方法对其进行恢复。
注意到打印台,只要头像先绘制,则海报绘制无效,与文档描述一致。
修改头像绘制函数即可:
toPaintAvatar ( ctx, canvas, avatar_url ) {
const avatarImg = canvas.createImage()
avatarImg.src = avatar_url;
avatarImg.onload = () => {
var avatarurl_width = 85; //绘制的头像宽度
var avatarurl_heigth = 85; //绘制的头像高度
var avatarurl_x = 145; //绘制的头像与画布左边缘的距离
var avatarurl_y = 45; //绘制的头像与画布上边缘的距离
ctx.save();
ctx.beginPath();
ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math.PI *2, false);
ctx.closePath()
ctx.clip();
ctx.drawImage(avatarImg, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth); // 推进去图片,必须是https图片
ctx.restore() // 完成绘制后恢复画布
console.log('toPaintAvatar')
}
},