1、资源准备
①海报背景图(建议放在不要放在本地)、
②头像(需要授权)、
③带参二维码、
④需要自定义展示的文字、小的icon图片
2、工具类,及方法
①兼容屏幕大小的rpx转px的方法(手机屏幕有差异,canvas用的是px为单位)
function createRpx2px() {
const { windowWidth } = wx.getSystemInfoSync()
return function(rpx) {
return windowWidth / 750 * rpx
}
}
②保存base64的图片到本地并返回本地文件路径(二维码返回的是base64)
const fsm = wx.getFileSystemManager();
const FILE_BASE_NAME = 'tmp_base64src';
export const base64src = function(base64data) {
return new Promise((resolve, reject) => {
let time = new Date().getTime()
let [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];
if (!format) {
reject(new Error('ERROR_BASE64SRC_PARSE'));
}
//加时间戳是为了图片的缓存问题
let filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME+time}.${format}`;
console.log(filePath)
let buffer = wx.base64ToArrayBuffer(bodyData);
console.log(buffer)
fsm.writeFile({
filePath,
data: buffer,
encoding: 'binary',
success() {
resolve(filePath);
},
fail() {
reject(new Error('ERROR_BASE64SRC_WRITE'));
},
});
});
};
③获取图片,获取本地图,网路图的信息=》提供给绘制canvasy用
getImageInfo(url) {
return new Promise((resolve, reject) => {
wx.getImageInfo({
src: url,
success(res) {
//如果是本地图片的话此api返回的路径有问题,所以需要判断是否是网络图片
if (!/^https/.test(url)) {
res.path = url
};
resolve(res)
},
fail(err) {
console.log('errimgurl', err)
reject(err.errMsg + `${url}`)
},
})
})
},
④计算文本长度(绘制canvas时是依据上左距离进行定位,同一行如果要接着文字绘制其他图文,就需要计算文字长度)
// 计算文本长度
calcTextLength(text){
let len = 0
for(let i =0;i<text.length;i++){
if(text.charCodeAt(i) > 255){
len +=2
}else{
len +=1
}
}
return len*15
},
⑤绘制圆形图片 一般头像需要绘制成圆形的
/**
*
* @param {*} contex
* @param {绘制的头像} img
* @param {x坐标} x
* @param {y坐标} y
* @param {直径} d
*/
circleImg(contex, img, x, y, d) {
let avatarurl_width = d; //绘制的头像宽度
let avatarurl_heigth = d; //绘制的头像高度
contex.save();
contex.beginPath(); //开始绘制
//先画个圆 前两个参数确定了圆心 (x,y) 坐标 第三个参数是圆的半径 四参数是绘图方向 默认是false,即顺时针
contex.arc(avatarurl_width / 2 + x, avatarurl_heigth / 2 + y, d / 2, 0, Math.PI * 2, false);
contex.clip(); //画好了圆 剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内 这也是我们要save上下文的原因
contex.drawImage(img, x, y, avatarurl_width, avatarurl_heigth); // 推进去图片,必须是https图片
contex.stroke();
contex.closePath();
contex.restore(); //恢复之前保存的绘图上下文 恢复之前保存的绘图上下午即状态 还可以继续绘制
},
⑥
/**
*
*保存canvas绘制的图像到临时目录
*/
canvasToTempFilePath(option, context) {
return new Promise((resolve, reject) => {
wx.canvasToTempFilePath({
...option,
success: resolve,
fail() {
console.log('canvasToTempFilePath', err)
reject(err)
},
}, context)
})
},
⑦ 获取二维码
getcodeImg() {
let data = {
"width": 430,
"scene": `c=${productCode}&j=${jobNumber}&m=${productModel}` //二维码的参数
}
if (process.env.NODE_ENV === 'production') {
data.page = "pages/index/index"
}
console.log('二维码参数', data)
return new Promise((reslove, reject) => {
app.$Http.post('/api/agentInfo/codeUnlimit', data).then(res => {
console.log('二维码res', res)
let base64 = res.content
let src = `data:image/png;base64,${base64}`
reslove(src)
}).catch(err => {
console.log('二维码err', err)
reject(err)
})
})
}
3 生成海报
下面绘制的只是一个思路具体还是要带入自己海报的需求,基本上的海报功能一般都能满足。
<view class="warp">
<!-- <p class="tip">保存图片到相册,然后发布朋友圈,好友就可以通过扫码进入投保页面;你也可以在客户动态中查看好友动态;</p> -->
<canvas class="canvas-hide" style="width: 686rpx; height: 1064rpx;" canvas-id="share"></canvas>
<image src="{{imgsrc}}" alt="" style="width: 686rpx; height: 1064rpx;" bindtap="previewImage"/>
<!-- <view class="savebtn" wx:if="{{imgsrc}}" >保存到相册</view> -->
</view>
.canvas-hide {
position: fixed;
top: 0;
left: 0;
transform: translateX(-100%);
width: 654rpx;
height: 1010rpx;
}
drawCanvas() {
//图片地址可以是本地相对路径,也可以是网络图片
let imagesArr = [
'./img/',
avatarUrl,
'./img/'
]
let QRcodereq = this.getcodeImg() //获取二维码
wx.showLoading({
title: '图片生成中...',
mask: true
})
Promise.resolve().then(() => {
//promise。all在这里可以保证所有图片都获取成功后,再开始进行canvas画图
//也可以添加一些其他promise请求,总之就是做一些前期准备,尽可能快的完成画图前的需要。
Promise.all([QRcodereq, ...imagesArr.map(item => {
return this.getImageInfo(item)
})]).then(([QRcodebase64, bg1_res, avatarUrl_res, bg2_res]) => {
base64src(QRcodebase64).then(QRcodeurl => {
//canvas的宽高
let canvasW = rpx2px(750)
let canvasH = rpx2px(1000)
//指定id为share 的canvas
const ctx = wx.createCanvasContext('share', this)
// 绘制背景色
ctx.fillStyle = "#33374F"
ctx.fillRect(0, 0, canvasW, canvasH) //
//画图片
ctx.drawImage(bg1_res.path, 0, 0, canvasW, rpx2px(300))
//画二维码
ctx.drawImage(QRcodeurl, 0, 0, rpx2px(300), rpx2px(300))
// 绘制文字
ctx.setFontSize(rpx2px(56))
ctx.setTextAlign('center')
ctx.setFillStyle('#FFD6A8')
ctx.fillText('文字' || '', rpx2px(370), rpx2px(350))
// 绘制头像 (放在最后画,放在前面会被后续影响,未知原因)
this.circleImg(ctx, avatarUrl_res.path, rpx2px(292), rpx2px(106), rpx2px(166))
ctx.draw(true, () => {
wx.hideLoading()
setTimeout(() => {
this.canvasToTempFilePath({
canvasId: 'share',
}, this).then(({
tempFilePath
}) => {
wx.hideLoading()
this.setData({
imgsrc: tempFilePath //这个就是生成的图片
})
})
}, 300)
})
})
}).catch(err => {
wx.hideLoading()
wx.showToast({
title: '图片生成出错:' + err,
icon: 'none'
})
})
})
}