小程序canvas绘制图片并保存

之前写过一篇小程序分享,体验不是很好。因为用户点击去分享时,canvas绘制并转换成图片需要一定的时间,用户视觉等待的时间有点长。为了更好的优化这一体验,我们可以这么做:
1、用户点击去分享,先用页面标签将海报图绘制出来;
2、当用户点击保存的时候,我们执行绘制canvas绘制,canvas转换图片,保存图片的过程。
一、用页面标签把海报图画出来,这个不需要讲解了,前端都能做到;
二、我们详细讲解一下canvas绘制的思路:
1、配置海报上的信息,需要绘制什么内容
在这里插入图片描述
2、用户点击保存按钮
// 点击保存事件
saveImgHandler() {
if(this.data.shareInviteImage){
this.saveInviteCanvas()
}else{
this.drawInviteCanvas(this.data.InviteOption).then( () => {
this.saveInviteCanvas() // canvas保存本地相册
})
}
},
// 绘制
drawInviteCanvas (params) {
let that = this
return new Promise(function (resolve, reject) {
CanvasInvite.call(that,params).then((url) => {
if (url) {
resolve(url)
that.setData({
shareInviteImage: url,
})
} else {
reject(0)
}
},this);
})
},
// canvas保存本地相册
saveInviteCanvas() {
let that = this
wx.showLoading({
title: ‘’,
mask:true
})
wx.saveImageToPhotosAlbum({
filePath:this.data.shareInviteImage,
success(res) {
App.isWritePhotosAlbum = true;
wx.hideLoading({})
},
fail(err) {
wx.hideLoading({})
App.isWritePhotosAlbum = false;
wx.getSetting({
success(res) {
if (!res.authSetting[‘scope.writePhotosAlbum’]) {
that.setData({
saveImgBtnHidden: false, // 控制保存按钮的显示
OpenSettingBtnHidden: true
})
} else {
}
}
})
}
},this)
},
3、绘制canvas方法drawInviteCanvas()可以单独写成一个js文件放在utils文件夹中共用
let ctx;
let shareTitLength = []; //活动名称长度
let windowW;
let ratio;
function drawInviteCanvas (params) {
// 获取屏幕尺寸宽高
windowW = wx.getSystemInfoSync().windowWidth;
ratio = windowW / 750 * 2;
console.log(‘windowW’,windowW,‘ratio’,ratio)
var that = this;
let promise1 = new Promise(function (resolve, reject) {
if (params.shareImage) {
wx.getImageInfo({
src: params.shareImage,
success(res) {
resolve(res);
}
})
} else {
resolve(0);
}

});
let promise2 = new Promise(function (resolve, reject) {
// 获取逗号背景
if (params.shareConImg) {
wx.getImageInfo({
src: params.shareConImg,
success(res) {
resolve(res);
}
})
}else {
resolve(0);
}
});
let promise3 = new Promise(function (resolve, reject) {
//获取二维码临时路径
if (params.qrCode) {
wx.getImageInfo({
src: params.qrCode,
success(res) {
resolve(res);
}
})
}else {
resolve(0);
}
});
return new Promise(function (resolve, reject) {
Promise.all([promise1, promise2, promise3]).then(res => {
ctx = wx.createCanvasContext(‘firstCanvas’,that);
ctx.save(); // 保存画笔状态
// 1. 绘制白色背景
ctx.setFillStyle(‘white’);
ctx.fillRect(0,0,375ratio,591ratio);
ctx.restore(); // 恢复 初始状态
ctx.save();
// 2.绘制大背景图
if (res[0] && res[0].path) {
roundRect(ctx, 0, 0, 375ratio, 185ratio, 10ratio, res[0].path)
ctx.restore(); // 恢复 初始状态
ctx.save();
}
// 3.绘制内容背景图
if (res[1] && res[1].path) {
roundRect(ctx, 26
ratio, 210ratio, 322ratio, 89ratio, 10ratio, res[1].path)
ctx.restore(); // 恢复 初始状态
ctx.save();
}
// 恢复画笔
ctx.restore(); // 恢复 初始状态
ctx.save();
//绘制 邀你来参加
drawText(’#fff’, 14 *ratio, ‘邀你来参加’, 322 * ratio, 65 * ratio, 64, 28 * ratio);
// 绘制標題
if (params.shareTit) {
shareTitLength = [];
var _y = (94 * ratio);
let _text1,len = params.shareTit.length
if (len > 30) {
_text1 = params.shareTit.substring(0,30)+ ‘…’
drawText(’#fff’, 18 *ratio, _text1, 250 * ratio, 65 * ratio, _y, 28 * ratio);
} else{
drawText(’#fff’, 18 *ratio, params.shareTit, 250 * ratio, 65 * ratio, _y, 28 * ratio);
}
}
// 绘制内容
if (params.shareContent) {
shareTitLength = [];
var _y = (234 * ratio);
let _text1,len = params.shareContent.length
if (len > 60) {
_text1 = params.shareContent.substring(0, 56)+ ‘…’
drawText(’#333’, 14 *ratio, _text1, 250 * ratio, 65 * ratio, _y, 28 * ratio);
} else{
drawText(’#333’, 14 *ratio, params.shareContent, 250 * ratio, 65 * ratio, _y, 28 * ratio);
}
}
// 报名时间
if (params.signUpStartTime){
let _y = (340 * ratio);
let _label = ‘报名时间’
drawText(’#333’, 12 *ratio, _label, 322 * ratio, 65 * ratio, _y, 30 * ratio);
let _y1 = (340 * ratio);
let _text = params.signUpStartTime + ’ - ‘+ params.signUpEndTime
drawText(’#333’, 12 *ratio, _text, 322 * ratio, 120 * ratio, _y1, 30 * ratio);

  }
  // 活动时间
  if (params.activityStartTime) {
    let _label = '活动时间'
    let _y = (365 * ratio);
    drawText('#333', 12 *ratio, _label, 322 * ratio, 65 * ratio, _y, 30 * ratio);
    let _y1 = (365 * ratio);
    let _text = params.activityStartTime + ' - '+ params.activityEndTime
    drawText('#333', 12 *ratio, _text, 322 * ratio, 120 * ratio, _y1, 30 * ratio);
  }
  // 6.绘制小程序二维码
  drawRoundedRect(ctx, 65 * ratio, 444 * ratio, 40 * ratio, 40 * ratio, 20 * ratio, true, false);
  drawCircle(64 * ratio, 64 * ratio, 65 * ratio, 444 * ratio, res[2].path, true);


  // drawCircle(68 * ratio, 68 * ratio, 243 * ratio, 428 * ratio, res[2].path, false);
  // 7.提示语
  let text1 = '长按识别二维码'
  drawText('#999', 12 * ratio, text1, 273 * ratio, 139 * ratio, 468 * ratio, 20 * ratio);
  let text2 = '即刻入营'
  drawText('#999', 12 * ratio, text2, 273 * ratio, 139 * ratio, 488 * ratio, 20 * ratio);

  ctx.draw(true, setTimeout(function () {
    wx.canvasToTempFilePath({
      x: 0,
      y: 0,
      fileType:'jpg',
      canvasId: 'firstCanvas',
      success(res) {
        console.log('success',res)
        resolve(res.tempFilePath)
      },
      fail(res){
        console.log('error',res)
      },
      complete(res){
      }
    },that)
  }, 1000));
})

})
}
//圆角背景图 *******************
function roundRect(ctx, x, y, w, h, r, imgPath) {
// 开始绘制 剪切
// ctx.clip()
ctx.drawImage(imgPath, x, y, w, h);
ctx.restore(); // 恢复 初始状态
ctx.save();
}
// 画圆形图
function drawCircle(circleW, circleH, circleX, circleY, imgPath, stroke) {
ctx.beginPath(); // 开始绘制
ctx.arc(circleW / 2 + circleX, circleH / 2 + circleY, circleW / 2, 0, Math.PI * 2, false); // 画圆
ctx.closePath();
ctx.clip(); //画好了圆 剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内 这也是我们要save上下文的原因
ctx.drawImage(imgPath, circleX, circleY, circleW, circleH); // 推进去图片,必须是https图片
ctx.scale(2, 2);
// 恢复画笔
ctx.restore(); // 恢复 初始状态
ctx.save();
}
//绘制内容
function drawText (color, ftSize, content, ctWidth, x, y, fontTop) {
ctx.setFillStyle(color) // 文字颜色:黑色
ctx.setFontSize(ftSize) // 文字字号:22px
// ctx.font = ‘normal 28px 思源黑体’
var text = content;
var temp = “”;
var row = [];
if(text.length > 1){
var chr = text.split(""); //这个方法是将一个字符串分割成字符串数组
for (var a = 0; a < chr.length; a++) {
if (ctx.measureText(temp).width < ctWidth) {
temp += chr[a];
} else {
a–; //这里添加了a-- 是为了防止字符丢失,效果图中有对比
row.push(temp);
temp = “”;
}
}
row.push(temp);
}else{
row.push(text);
}
var _shareTitLength = row.length;
shareTitLength.push(_shareTitLength)
for (var b = 0; b < row.length; b++) {
// 1.文本内容 2.水平参考点位置 3.垂直参考点位置 4.文本内容占据的最大宽度
ctx.fillText(row[b], x, y + b * fontTop, ctWidth);
}
// 恢复画笔
ctx.restore(); // 恢复 初始状态
ctx.save();
}
// 绘制圆角矩形
function drawRoundedRect (ctx, x, y, width, height, r, fill, stroke) {
ctx.save();
ctx.beginPath(); // draw top and top right corner
ctx.moveTo(x + r, y);
ctx.arcTo(x + width, y, x + width, y + r, r); // draw right side and bottom right corner
ctx.arcTo(x + width, y + height, x + width - r, y + height, r); // draw bottom and bottom left corner
ctx.arcTo(x, y + height, x, y + height - r, r); // draw left and top left corner
ctx.arcTo(x, y, x + r, y, r);
ctx.closePath();
if (fill) {
ctx.setFillStyle(‘white’);
ctx.fill();
}
if (stroke) {
ctx.shadowBlur = 15; // 设置模糊度
ctx.shadowColor = “rgba(0,0,0,0.8)”; // 设置阴影颜色
ctx.strokeStyle = ‘#fff’;
ctx.stroke();
}
ctx.restore();
}

export default drawInviteCanvas;