今天设计师给了一张背景图和一个头像框图标和头像图标,要求通过几个图标画成一张新图并在头像框中写上获奖者的名字
实现思路:由于canvas生成后样式比较难调,所以通过canvas生成图片,然后隐藏canvas,生成的图片通过image标签在页面中显示,通过imgage去调节定位间距等样式
canvas生成:
- canvas的宽度固定,高度按照图片宽高比剩宽度得出
- 如果canvas宽度设置750px 图片宽高比为1 则canvas高度为750px
- 生成的图片根据image的使用小程序提供的widthFix样式,宽度不变,高度自适应
- canvas作用只是负责生成一个原图比例相同的图片
<canvas style="width: {{canvasX}}px; height: {{canvasY}}px" canvas-id="canvas"></canvas>
- 由于设计稿给的大小是750px,那么我将canvas的宽度设置为750px
data: {
file:'https://qfd.oss-cn-guangzhou.aliyuncs.com/2021/static/UI/20220320002.png',
file1:'https://qfd.oss-cn-guangzhou.aliyuncs.com/2021/static/UI/20220320004.png',
avatar:'https://qfd.oss-cn-guangzhou.aliyuncs.com/static/faceframe.png'
,avatarImg: '/image/student.png',
direction:true//true为纵向,false为横向
},
onLoad: function (e) {
this.setData({
canvasX:375,//画布宽度固定为375
})
this.setTempFile()
}
- 直接使用网络图片无法绘制canvas的背景(不会显示没有效果)下图是网络图片设置背景,大家可以试一下
canvas.drawImage('https://baidu.png',0, 0,150, 150);
- 由于背景图和头像框是网络图片,无法通过canvas直接画出,要转成临时图片,因为getImageInfo是异步请求,所以需要
setTempFile:async function(){
let bgImg = await this.getFile()
let avatar = await this.getFile1()
this.setData({
bgImg:bgImg,//背景图临时地址
avatar:avatar//头像框临时地址
})
this.drawImg()
}
- 将请求放在一个promise中,等请求结束后再将路径返回设置到上面data中,通过返回的res获得图片的宽高,再根据宽高比,生成canvas的高度。
getFile:function(){
return new Promise((resolve,reject)=>{
let bgImg = this.data.file//网络图片
let that = this
wx.getImageInfo({ // 根据地址下载并存为临时路径,网络地址的路径无法画到画布中
src: bgImg,
success: res => {
that.setData({
canvasY:that.data.canvasX*(res.height/res.width),//设置canvas的高度
})
resolve(res.path)
}
})
})
},
- 从上面的步骤已经拿到了背景图和头像框的临时图片,头像图片是本地地址
- 下面开始绘制到画布上 drawImage参数为(地址,起点left,起点top,拉伸宽度,拉伸长度)
- 由于画布的比例和背景图比例一样,所以从(0,0)起点开始,然后拉伸画布的长度宽度
- 效果是铺满整个画布,比例相同的条件下能够保证图片不被拉伸
//将临时图片绘制到画布上
drawImg:function(){
let canvas = wx.createCanvasContext('canvas');
canvas.drawImage(this.data.bgImg,0, 0, this.data.canvasX, this.data.canvasY);
canvas.save();//绘制第二张图片应该先保存一下之前绘制的图片
// //绘制头像定位
let imgiconW = 70//头像高度
let imgiconH = 70//头像宽度
let imgiconX = (this.data.canvasX - 70) / 2//画布宽度减去自身宽度除2,得出左边距离
let imgiconY = 100
canvas.drawImage(this.data.avatarImg,imgiconX,imgiconY,imgiconW,imgiconH);
// //绘制头像框定位
let iconW = 150
let iconH = 95
let iconX = (this.data.canvasX - 150) / 2
let iconY = 100
canvas.drawImage(this.data.avatar,iconX,iconY,iconW,iconH);
// //绘制文字定位 先改变中心 保持居中
let fontVal = 15+"px serif"
let fontX = 375/2//取画布x轴中心,画出通过下面文字居中,能达到文字在画布x轴居中的效果
let fontY = 175
canvas.font= fontVal;
canvas.textAlign = 'center';
canvas.fillText("Hello World",fontX,fontY);
let that = this
//将生成好的图片保存到本地,需要延迟一会,绘制期间耗时
canvas.draw(true, setTimeout(function () {
that.toImport()
}, 1000));
},
- 需要注意的是:
- 先画头像,再画头像框,drawImage的调用顺序决定了图片的层级,越慢调用层级越高
- 图片居中定位使用画布宽度减去自身宽度除2,得出左边距离
- 文字居中定位使用画布宽度除2,得出中间点距离,再用textalign中间点居中
- 将生成好的图片保存到本地,需要延迟一会,绘制期间耗时
- 最后通过将画布的图片导出图片路径,放到image标签中进行显示,之前的canvas直接隐藏掉,这里笔者是通过定位的方式隐藏画布,有更好的方法可以评论区提出
- 如果通过hidden的话会导致获取不到canvas所以不能用这个额方法
<view >
<canvas style="width: {{canvasX}}px; height: {{canvasY}}px;position: fixed;top:-800px;" canvas-id="canvas"></canvas>
<view>
<image class="imgClass" mode='widthFix' src="{{imgSrc}}"></image>
</view>
</view>
toImport:function (params) {
let that = this
wx.canvasToTempFilePath({
x: 0,
y: 0,
canvasId: 'canvas',
fileType: 'png', //设置图片类型,否则图片无背景色
success(res) {
that.setData({
imgSrc:res.tempFilePath
})
}
})
}
- 最后实现效果如下