场景:画布在小程序的分享海报中是很常见的功能,如果遇到内容过多的时候可以使用第三方插件如html2canvas。本文记录下原生计算的方式:
1.wxml:
<view class='canvas-container' id='canvas-container'>
<canvas canvas-id="myCanvas"/>
</view>
2.wxss:
.canvas-container {
position: fixed;left: 0 ;right: 0;z-index: -99999999;
width: 171px;height: 305px;
margin: 0 auto;
}
.canvas-container canvas {width:100%;height:100%;}
3.js:
①关于响应式问题:定义一个响应比例,后期通过设计稿量出来的尺寸要进行比例相乘达到适配不同尺寸手机效果。
wx.createSelectorQuery().select('#canvas-container').boundingClientRect(function (rect) {
let rpx = 1;
wx.getSystemInfo({
success(res) {
rpx = res.windowWidth / 375;
},
})
}).exec()
②关于文字居中问题:使用API获取文字宽度来获取文字显示在x轴的距离
let str = '我是一串文字';
ctx.fillText(str, ((171 - ctx.measureText(str).width) / 2)*rpx, 20*rpx);
③关于头像裁剪成圆形问题:
ctx.arc(圆心的 x 坐标,圆心的 y坐标,圆的半径,起始弧度,终止弧度,弧度的方向是否是逆时针)
ctx.arc( ((171 - ctx.measureText(str).width) / 2 ) * rpx, 14 * rpx, 6 * rpx, 0, Math.PI * 2, false);
ctx.clip();
ctx.drawImage(headImg, ((171 - ctx.measureText(str).width) / 2 - 6)*rpx, 8*rpx, 12*rpx, 12*rpx)
④关于绘制网络图片问题:
资源为网络图片的话需要通过 downloadFile 先下载。
绘制图像到画布有三个版本的写法(具体详见文档):
- drawImage(imageResource, dx, dy)
- drawImage(imageResource, dx, dy, dWidth, dHeight)
- drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) 从 1.9.0 起支持
当有一张背景图需要绘制在画布的正中间时,可能遇到图片需要缩放问题:
思路:1.获取图片得宽高 2.获取画布的宽高 3.计算图片的缩放比例以及位置
wx.getImageInfo({
src: cardbg,
success (res) {
let imgW = res.width; //获取到图片宽度
let imgH = res.height; //获取到图片高度
let canvasWidth = rect.width; //画布宽(通过createSelectorQuery中获取到)
let canvasHeight = rect.height; //画布高
let x=0;
let y=0;
if(canvasWidth/imgW>canvasHeight/imgH){
imgScale = canvasWidth/imgW
y= -(imgScale*imgH-canvasHeight)/2
}else{
imgScale =canvasHeight/imgH
x= -(imgScale*imgW-canvasWidth)/2
}
ctx.drawImage(cardbg,0,0, imgW,imgH,x, y,imgScale*imgW, imgScale*imgH);
})
⑤多张网络图片下载处理方式:Promise.all([url1,url2,..]) 返回的结果与数组中的序列号是对应的,以此类推使用
Promise.all([
that.downloadImage(url1), //调用方法获取到返回路径
that.downloadImage(url2),
]).then((result)=>{
that.getCanvas(result[0], result[1])
})
downloadImage(url){ //下载网络图片方法
return new Promise(function(resolve){
wx.downloadFile({
url,success:function(res){
if (res.statusCode === 200) { resolve(res.tempFilePath) }
},
})
})
},
展示效果图:
实现代码:
onLoad() {
let that = this;
let { headImg, codeImg } = that.data;
Promise.all([
that.downloadImage(headImg),
that.downloadImage(codeImg),
]).then((result) => {
that.getCanvas(result[0], result[1])
}).catch((err) => {
console.log(err)
})
},
downloadImage(url) {
return new Promise(function (resolve) {
wx.downloadFile({
url,
success: function (res) {
if (res.statusCode === 200) {
resolve(res.tempFilePath)
}
},
})
})
},
getCanvas(headImg, codeImg ) { //绘制画布
let that = this;
let { cardbg } = that.data;
const ctx = wx.createCanvasContext('myCanvas'); //创建画布
wx.createSelectorQuery().select('#canvas-container').boundingClientRect(function (rect) {
let rpx = 1;
wx.getSystemInfo({
success(res) {
rpx = res.windowWidth / 375;
wx.getImageInfo({
src: cardbg,
success(res) {
let imgScale = 1;
let imgW = res.width; //图片宽度
let imgH = res.height; //图片高度
let canvasWidth = rect.width; //画布宽度
let canvasHeight = rect.height * .777; //画布高度(是否有比例按照实际需求来)
let x = 0;
let y = 0;
let imgScalew = canvasWidth / imgW;
let imgScaleh = canvasHeight / imgH;
if (imgScalew > imgScaleh) {
imgScale = imgScalew
y = -(imgScale * imgH - canvasHeight) / 2
} else {
imgScale = imgScaleh
x = -(imgScale * imgW - canvasWidth) / 2
}
//背景图
ctx.drawImage(cardbg, 0, 0, imgW, imgH, x, y, imgScale * imgW, imgScale * imgH);
ctx.setFontSize(14 * rpx);
ctx.setFillStyle('#3D3D3D');
ctx.fillText("我是昵称", 50 * rpx, (474 / 2 + 34) * rpx);
// 绘制头像
ctx.save();
ctx.beginPath();
ctx.arc(24 * rpx, 271 * rpx, 24 * rpx, 0, Math.PI * 2, false);
ctx.clip();
ctx.drawImage(codeImg, 0, 247 * rpx, 48 * rpx, 48 * rpx)
}
})
},
})
}).exec()
setTimeout(function () {
ctx.draw();
}, 1000)
},
saveShareImg: function () {
wx.showLoading({
title: '海报生成中...',
mask: true,
})
setTimeout(function () {
wx.canvasToTempFilePath({
canvasId: 'myCanvas',
success: function (res) {
wx.hideLoading();
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success(res) {
wx.showModal({
content: '图片保存到相册',
showCancel: false,
confirmText: '我知道了',
success: function (res) {
if (res.confirm) {}
},
fail: function (res) {}
})
},
fail: function (res) {
console.log(res)
if (res.errMsg === "saveImageToPhotosAlbum:fail:auth denied") {
console.log("打开设置窗口");
wx.openSetting({
success(settingdata) {
if (settingdata.authSetting["scope.writePhotosAlbum"]) {
console.log("获取权限成功,再次点击图片保存到相册")
} else {
console.log("获取权限失败")
}
}
})
}
}
})
},
fail: function (err) {
console.log(err)
}
});
}, 1000);
},
常规画布需求实现完毕,后期有额外的需求再补充实现。