程序小白从0开始的canvas踩坑
逻辑:循环将多条留言绘制在图片上,形成海报
下面代码中 this.data.selectMessage 为图片的二维数组,包括头像、背景图、留言背景图、微信昵称、留言内容、权重等字段
踩坑1、image.onload为异步执行,每个数组中包含微信头像、留言背景图两个图片,还要递归循环。因为异步所以导致图片缺失、递归无法正常执行。所以将数组从[a,b,c]整合成[a,a,b,b,c,c]在图片下载回调中循环嵌套绘制留言背景图和头像
踩坑2、头像为圆形设计所以需要裁剪一下代码为完整代码。有效避免裁剪的各种缺失、或者头像无法正常绘制
ctx.save(); //当前区域保存
ctx.beginPath(); //开始新的区域
ctx.arc(x+9+8, y+12+top+8, 8, 0, 2 * Math.PI, false) //画一个圆形裁剪区域
ctx.clip() //画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
ctx.drawImage(image, x+9, y+12+top,16,16); //在留言背景图上绘制头像
ctx.restore(); //恢复状态
踩坑3、
整理后完整代码如下、变量会有缺失、自己按照逻辑调整自己的变量
<canvas type="2d" id="mycanvas" style="width: 375px;height: 812px;"></canvas>
//将canvas转换为图片保存到本地,然后将图片路径传给image图片的src
createNewImg: function () {
var that = this;
// 根据Index进行排序确定层级
this.data.selectMessage.sort((a, b) => {
return a.index > b.index ? 1 : -1;
})
var data_list = this.data.selectMessage
wx.createSelectorQuery()
.select('#mycanvas') // 在 WXML 中填入的 id
.fields({ node: true, size: true })
.exec((res) => {
// Canvas 对象
const canvas = res[0].node
// 渲染上下文
const ctx = canvas.getContext('2d')
// Canvas 画布的实际绘制宽高
const width = 375
const height = 812
// 初始化画布大小 调整IOS不同倍数的高清屏
const dpr = wx.getWindowInfo().pixelRatio
canvas.width = width * dpr
canvas.height = height * dpr
const top = height * 0.256
ctx.scale(dpr, dpr)
//画入图片
const image = canvas.createImage()
// 设置图片src
image.src = this.data.bgimg
// 图片加载完成回调
image.onload = () => {
// 将图片绘制到 canvas 上
ctx.drawImage(image, 0, 0,width,height)
if(data_list.length > 0){
var all_image = [];
var key_count = 0;
for (let index = 0; index < data_list.length; index++) {
all_image[key_count] = data_list[index];
key_count++;
all_image[key_count] = data_list[index];
key_count++;
}
this.onload_list(ctx,image,all_image,0,canvas,top);
}
}
})
},
onload_list:function (ctx,image,data_list,all_count,canvas,top) {
all_count++;
//取出数组第一个元素并删除第一个元素
const image_data = data_list.shift();
const content = image_data['name'];
let x = parseFloat(image_data['xxx']) * 812 / this.data.view_height;
let y = parseFloat(image_data['yyy']) * 812 / this.data.view_height;
let user_name = image_data['user_name'];
const user_image = image_data['user_image'];
const img_url = image_data['image'];
//判断x y值。防止出界
if(x > 258){
x = 258;
}
if(y > 304){
y = 304
}
if(user_name.length > 9){
user_name = user_name.substr(0,9)+'...';
}
if(all_count%2 == 1){
//绘制留言背景图
image.src = img_url;
image.onload = () => {
ctx.drawImage(image, x, y+top,120,117);
//执行递归程序,如果数量大于0 那么继续执行。如果小于0 则不递归
if(data_list.length > 0){
this.onload_list(ctx,image,data_list,all_count,canvas,top); //递归调用
}
}
}else{
ctx.fillStyle= "#000";
ctx.font = "8px PingFangSC-Medium";
ctx.fillText(user_name,x+27,y+top+23); //绘制微信昵称
this.drawTextOn(ctx,content,x+18,y+top+30,80) //绘制留言内容
//绘制头像
image.src = user_image;
image.onload = () => {
ctx.save(); //当前区域保存
ctx.beginPath(); //开始新的区域
ctx.arc(x+9+8, y+12+top+8, 8, 0, 2 * Math.PI, false) //画一个圆形裁剪区域
ctx.clip() //画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
ctx.drawImage(image, x+9, y+12+top,16,16); //在留言背景图上绘制头像
ctx.restore(); //恢复状态
//执行递归程序,如果数量大于0 那么继续执行。如果小于0 则不递归
if(data_list.length > 0){
this.onload_list(ctx,image,data_list,all_count,canvas,top); //递归调用
}else{
wx.canvasToTempFilePath({
canvas,
success: function (res) {
var tempFilePath = res.tempFilePath;
var image_list = [];
image_list.push(tempFilePath);
wx.previewImage({
current: tempFilePath, // 当前显示图片的http链接
urls: image_list // 需要预览的图片http链接列表
});
wx.hideLoading()
},
fail: function (res) {
console.log(res);
}
}, this);
}
}
}
},
// canvas填充文字自动换行
drawTextOn(ctx,t,x,y,w){
var chr = t.split("");
var temp = "";
var row = [];
var line_spacing = 20;
ctx.font = "14px PingFangSC-Medium";
ctx.fillStyle = "#000";
if(chr.length <= 6){
y = y+25;
}else if(chr.length > 6 && chr.length <= 12){
y = y+15;
}else if(chr.length > 12 && chr.length <= 18){
y = y+5;
}else if(chr.length > 18 && chr.length <= 24){
}else if(chr.length > 24 && chr.length <= 30){
y = y+12;
ctx.font = "8px PingFangSC-Medium";
line_spacing = 15;
}else if(chr.length > 30 && chr.length <= 40){
y = y+5;
ctx.font = "8px PingFangSC-Medium";
line_spacing = 15;
}else{
ctx.font = "8px PingFangSC-Medium";
line_spacing = 15;
}
for(var a = 0; a < chr.length; a++){
if( ctx.measureText(temp).width < w ){
;
}
else{
row.push(temp);
temp = "";
}
temp += chr[a];
}
row.push(temp);
for(var b = 0; b < row.length; b++){
ctx.fillText(row[b],x,y+(b+1)*line_spacing);
}
},
//点击生成
saveImg: function (e) {
var that = this;
this.setData({
maskHidden: false
});
wx.showLoading({
title: '海报生成中...',
icon: 'loading',
});
that.createNewImg();
},