先交代一下背景,最近做一个微信小程序的项目,项目中有一个需求,就是生成日签和海报功能,最开始想通过微信小程序自带的canvas来实现,实际实现中,效果很不理想,通过drawImage写入到canvas中图片,不是显示不全,就是会出现很模糊的问题,总之距离想要达到的效果相距甚远,不断的查询资料中,找到了一个叫 wx-canvas-2d 的插件,基本上可以满足需求,接下来又遇到一个问题,需求是想生成一个1500*2400的海报,所以最开始是想,用两个canvas,一个用于显示给用户看,一个用生成下载,显示给用户看到背景图用的是300*480的背景图,下载用的背景图是1500*2400的,在电脑端是没有问题的,但是在wx.canvasToTempFilePath保存海报到手机默认相册时会失败,经测试发现,是因为生成的图片太大,导致保存失败,最后用显示给用户看那个canvas来生成海报保存,导出的尺寸可以设置成1500*2400,这样基本就满足需求了,下面上干货。
先上一下效果图。
1.第一步,使用 如何命令进行安装,当然前提是安装了npm的前提下,微信小程序中npm的安装以及需要注意的问题,会在另一篇文章详细介绍。
npm i -S wx-canvas-2d
2.安装成功后,在下面两个文件夹目录下面会有wx-canvas-2d的文件夹
3 安装成功后,到微信开发工具的工具选项里面点一下构建npm。
4.在wxml中写入canvas元素,本项目的需求在弹窗中显示海报,所以写了一个遮罩层,用了position:fixed;这个属性,但是有一个问题就是canvas在父级元素为position:fixed的情况下,会浮动出父级元素,跟随页面滑动,这显然不是想要的需求,最后只能不把canvas放到遮罩层里面,然后通过wxss来调整到想要显示的位置。
<!--遮罩层开始-->
<block wx:if="{{posters}}" >
<view id="shareshadwinfo" class="shareshadw" catchtouchmove="noneEnoughPeople" >
<!--日签生成开始-->
<block wx:if="{{posteritem}}" >
<view class="itemctn" >
<!--图片范围开始-->
<view class="topimgctn" style="height:{{changeheightctn}};" >
<!--画布开始-->
<!--画布结束-->
<!--图片范围结束-->
</view>
<view class="bottomselect">
<view class="itemsavctn" bindtap="saveshareimg" >
<view class="itemingctn"> <image class="img" src="../../style/images/wxicon.png" > </image> </view>
<view class="itemword" > 保存到相册</view>
</view>
<view class="itemsavctn itemsavctn-r" bindtap="saveshareimg" >
<view class="itemingctn"> <image class="img" src="../../style/images/wxicon.png"> </image> </view>
<view class="itemword"> 分享海报</view>
</view>
</view>
</view>
</block>
<!--日签生成结束-->
</view>
</block>
<!--遮罩层结束-->
<block wx:if="{{posters}}" >
<view id="canvas-containershow" class="canvas-containershow" style="height:{{changeheight}};" >
<canvas
type="2d"
id="poster-canvasshow"
class="poster-canvasshow"
style="width: 100%; height: 100%;margin:0 auto;"
disable-scroll="{{ true }}"
/>
</view >
</block>
5.然后就是写样式文件了,由于这里面的样式是根据我的项目需求所写的,不一定适用所有人,可能需要根据实际情况进行调整。
/*海报相关样式文件开始*/
.shareshadw{
position:fixed;
width:99%;
left:0;
right:0;
top:0;
bottom:0;
z-index:99;
background:rgba(0,0,0,0.55);
}
.itemctn{
width:100%;
height:100%;
}
.topimgctn{
width:85%;
/* height:72%;*/
margin:0 auto;
margin-top:80px;
}
.topimgctn .fooimg{
width:100%;
height:100%;
}
.bottomselect{
display:flex;
width:85%;
height:40px;
margin:10px auto;
}
.itemsavctn{
display:flex;
width:115px;
height:40px;
/*margin:0 10px;*/
margin:auto;
border-radius:50px;
padding:0 10px;
border:1px solid #eee;
}
.itemingctn{
width:25px;
height:25px;
margin:8px 0;
}
.itemingctn .img{
width:100%;
height:100%;
}
.itemword{
width:85px;
height:40px;
line-height:40px;
text-align:left;
color:#fff;
margin:0 3px;
font-size:15px;
}
.itemsavctn-r{
background:#07CF9F;
border:1px solid ##07CF9F;
}
.canvas-containershow{
position:relative;
width:79%;
margin:0 auto;
margin-top:-1010px;
z-index:999;
}
.canvas-container {
margin-top:-100000px;
width:1755rpx;
height:2810rpx;
}
/*海报相关样式文件结束*/
6.在 js
文件中引入 wx-canvas-2d
插件,然后实例化 WxCanvas2d
对象,并创建画布
import WxCanvas2d from 'wx-canvas-2d' //引入wx-canvas-2d
const canvasshow = new WxCanvas2d() //实例化对象
//获取背景图 创建画布
savelocalimg:function(e){
var that=this;
const db = wx.cloud.database()
//获取日签海报图开始
//此项目使用的是微信小程序的云开发系统,所以把海报的背景图放进了云开发的内容管理系统里面进行管理,需要先从后台获取背景图信息
db.collection('signaturebg').orderBy('_id', 'desc')
.get({
success: function(res) {
if(res.data.length){
//这一块是控制根据需求显示和隐藏遮罩层的。
that.setData({
sigimgurl:res.data[0].rqbgimg,
qgcodeimgurl:res.data[0].qgcodeimg,
})
wx.showLoading({
title: '日签生成中',
duration: 1000,
});
//由于日签中需要用的当日的时间,所以通过util.js这个插件对要用到的时间进行处理。
var todayxq='';
var todaybirtshxq=''
//const util = require('../../utils/util.js')
//获取设备宽度开始
var timestamp=new Date();
var xqday=timestamp.getDay();
if(xqday==1){
todayxq='周一';
todaybirtshxq='Monday'
}
if(xqday==2){
todayxq='周二';
todaybirtshxq='Tuesday'
}
if(xqday==3){
todayxq='周三';
todaybirtshxq='Wednesday'
}
if(xqday==4){
todayxq='周四';
todaybirtshxq='Thursday'
}
if(xqday==5){
todayxq='周五';
todaybirtshxq='Friday'
}
if(xqday==6){
todayxq='周六';
todaybirtshxq='Saturday'
}
var todaytmboard=timestamp.getFullYear()+'年'+(timestamp.getMonth()+1)+'月'+timestamp.getDate()+'日 '+todayxq;
var leidkdaynum=that.data.ljdkdays;
that.setData({
todaytmboardval:todaytmboard,
})
//console.log('海报时间'+todaytmboard)
//滚动轴滑到顶部开始
wx.pageScrollTo({
scrollTop: 0
})
//滚动轴滑到顶部结束
//这一块是根据不同的设备的动态调整画布的高度
var showcanvasheight=0;
wx.getSystemInfo({
success: function (res) {
let clientHeight = res.windowHeight;
let clientWidth = res.windowWidth;
let changeHeight = 375 / clientWidth;
let height = clientHeight * changeHeight;
var shebeiwidth=res.windowWidth
var multiples=(clientWidth/320).toFixed(1);
// showcanvasheight=((clientWidth/320).toFixed(1)*408).toFixed(0);
showcanvasheight=((clientWidth/320).toFixed(1)*410).toFixed(0);
var showcanvasheightval=showcanvasheight+'px';
var showcanvasheightctnval=(parseInt(showcanvasheight)+(25*multiples))+'px';
that.setData({
changeheight: showcanvasheightval,
changeheightctn: showcanvasheightctnval,
});
}})
//获取设备宽度结束
that.setData({
posters:true,
posteritem:true,
})
//海报显示开始
// 创建
canvasshow.create({
query: '.poster-canvasshow', // 必传,canvasshow元素的查询条件
rootWidth: 375, // 参考设备宽度 (即开发时UI设计稿的宽度,默认375,可改为750),这个参数用于可以控制图片背景在海报中显示的范围,比如如果背景图是1500*2400的,就需要把这个值调大一些,不然背景图会在画布中显示的不完整。
//bgColor: '#ff0000', // 背景色,默认透明
bgColor: '#ffffff', // 背景色,默认透明
component: that, // 自定义组件内需要传 this 如果该方法不是在函数的最外层,就要用that,用this无法绘制成功。
// radius: 16 // 海报图圆角,如果不需要可不填
}).then(res => {
// console.log(res)
}).catch(err => {
console.log('[Wxcanvasshow2d] canvasshow create fail: ', err)
})
// 绘制
canvasshow.draw({
series: [
{
type: 'image', // 图片
url: that.data.sigimgurl, //背景图地址
// url:'https://6a73-jsxzhj-9gn2l3gp6081732a-1305238372.tcb.qcloud.la/small.jpg?sign=e6f35655710038a889d5a9634efdf102&t=1617937394',
x: 0,
y: 0,
width: 300,
height: 480,
mode: 'aspectFill' // 图片的裁剪方式,参考小程序 image 标签的 mode 属性
},
{
type: 'text',
x:35,
y:45,
text: '一/起/暴/瘦',
color:'#fff',
fontSize: 25,
lineHeight:'20',
},
{
type: 'text',
x:30,
y:70,
text: '自控力! 自律给我自由',
color:'#fff',
fontSize: 15,
lineHeight:'42',
},
{
type: 'text',
x:90,
y:132,
text: '已经坚持打卡(天)',
color:'#fff',
fontSize: 18,
lineHeight:'42',
},
{
type: 'text',
x:110,
y:240,
text: leidkdaynum,
color:'#fff',
fontSize: 100,
fontWeight:'bold',
lineHeight:'42',
},
{
type: 'text',
x:50,
y:329,
text: todaytmboard,
color:'#fff',
fontSize: 15,
lineHeight:'30',
},
{
type: 'text',
x:30,
y:359,
text: 'You are',
color:'#fff',
fontSize: 12,
lineHeight:'42',
},
{
type: 'text',
x:30,
y:389,
text: 'Backlit with all the good',
color:'#fff',
fontSize: 12,
lineHeight:'30',
},
{
type: 'text',
x:30,
y:409,
text: 'things in this good',
color:'#fff',
fontSize: 12,
lineHeight:'30',
},
{
type: 'text',
x:200,
y:415,
text: '长按识别',
color:'#fff',
fontSize: 13,
lineHeight:'25',
},
{
type: 'image', // 图片
url: that.data.qgcodeimgurl, 这个是小程序二维码地址
// url:'https://6a73-jsxzhj-9gn2l3gp6081732a-1305238372.tcb.qcloud.la/qrcode.jpg?sign=4bc6492414feba96d9bcd474616a9047&t=1617854068',
x: 200,
y: 360,
width: 50,
height: 50,
mode: 'aspectFill' // 图片的裁剪方式,参考小程序 image 标签的 mode 属性
},
{
type: 'text',
x:180,
y:435,
text: '一起享 "瘦" 生活',
color:'#fff',
fontSize: 13,
lineHeight:'25',
},
]
}).then((res) => {
console.log('绘制成功!')
console.log(res)
}).catch(err => {
console.log('绘制失败!', err)
})
//海报显示结束
}
else{
console.log('无海报背景图数据')
}
}
})
//获取日签海报背景图结束
},
//保存生成的海报到手机默认相册。
saveshareimg:function(){
var that=this;
//console.log('保存图片')
//获取相册授权后开始执行结束
wx.getSetting({
success(res) {
if (!res.authSetting['scope.writePhotosAlbum']) {
wx.authorize({
scope: 'scope.writePhotosAlbum',
success () {
// conosle.log('用户授权相册功能')
// 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
// wx.startRecord()
}
})
}
else{
//测试待删除开始
wx.canvasToTempFilePath({ //使用这个方法,如果在微信开发工具里面能正常调用,在手机端就不行,可能是生成的海报过大的问题,一般不要超过2MB吧,该方法的目的只要获取海报的临时地址提供给wx.saveImageToPhotosAlbum调用,这个方法只接受本地绝对路径和临时地址,不接受网络地址或其他地址
canvas:canvasshow.canvas, //这里需要注意的是,可以传canvasid,但是如果canvas里面有了type=2d这个参数,在传canvasid就不起作用了,但是这个插件又是用到type=2d这个属性写的,无法去掉,所以只能穿canvas对象,wx-canvas-2d这个插件的cnavas对象在实例canvasshow的canvas里面方法,不是实例后的对象canvasshow,这点需要注意,第一次使用可能会遇到这个问题。
destWidth:1500, //输出的海报高度
destHeight:2400, //输出的海报宽度,
success: (res) => {
console.log(res)
//执行保存图片到本地开始
wx.saveImageToPhotosAlbum({
// filePath: res.tempFilePath,
// filePath: '/style/picture/gif1.jpg',
// filePath: '/style/picture/23.png',
filePath: res.tempFilePath,
success(res) {
//console.log('保存图片到相册res值');
//console.log(res)
that.setData({
posters:false,
posteritem:false,
})
},
fail:function(err){
console.log('保存失败')
console.log(err)
wx.showToast({
title: "您取消了保存图片",
duration: 2000
})
that.setData({
posters:false,
posteritem:false,
})
}
})
//执行保存图片到本地结束
},
fail:function(err){
//console.log('获取图片临时路径失败')
console.log(err)
}
})
}
},
fail(err){
console.log('失败')
console.log(err);
}
})
//获取相册授权后开始执行结束
// 保存图片到相册
},
7,虽然实现了,但是依然有一个小问题没解决,就是背景图不能在所有设备上都完全铺满画布,所以在某些设备上导出的海报底部可能会有白色背景。