微信小程序中生成图片并保存到本地的功能很普遍,但是由于第一次完全自己独立写,所以走了很多冤枉路,这里记录一下给需要帮助的人。
先说下思路:
1⃣️ 都知道canvas绘制图片是需要时间的,所以为了提高用户体检,我在一打开有分享按钮的页面就请求绘制图片需要的数据,开始绘制分享的图片
2⃣️ 在用户点击分享按钮的时候(此时canvas已经绘制完毕)弹出一个代替canvas分享图的html,因为html更好控制样式
3⃣️ 弹出以后,当用户点击保存时,调用微信提供的保存到相册的API完成保存✅
有了思路开始写代码呗
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 获取用来canvas绘制需要的用户信息
this.getUserCanas()
},
getUserCanas() {
var that = this
// req.createCanvasCard是自己封装的请求方法
req.createCanvasCard('接口参数').then(res => {
this.setData({
pyqInfo: res.result // 拿到数据赋值
})
var chapterUrl = '自己拼接的http地址' + res.result.chapterCover // 用户头像
wx.downloadFile({ // canvas不能绘制网络图,先将用户头像下载得到临时的本地地址
url: chapterUrl,
success (res) {
that.setData({
chapterCover: res.tempFilePath
})
that.createCanvas() // cancas绘制图片的方法
}
})
})
// 至此完成,获取到canvas绘制需要的用户信息
},
createCanvas() {
var content = this.data.pyqInfo.cardContent
var title = this.data.pyqInfo.chapterTitle
var studyChapterNum = this.data.pyqInfo.studyChapterNum
var hours = this.data.pyqInfo.hours
var minutes = this.data.pyqInfo.minutes
var portraitUrl = that.data.pyqInfo.portrait
// 以上分享图要展示的用户数据(也可以直接在画的时候this.data.xxx)
var unit = ''
wx.getSystemInfo({
success: function (res) {
unit = res.windowWidth/375
},
})
// 以上是为了canvas适配手机,在使用时, 宽 || 高 *unit
var that = this
var portrait
wx.downloadFile({
url: portraitUrl, // 这里是分享图需要的封面图片
success (res) {
that.setData({
portrait:res.tempFilePath
})
// 以下在wx.downloadFile成功的回调中开始画图
const ctx = wx.createCanvasContext('shareFrends')
ctx.beginPath() // 开始创建一个路径
ctx.drawImage('../../img/pyq.jpg',0,0, 375*unit, 540*unit)// 填充图片路径,此处是整个分享图的背景图片
that.roundRect(ctx, 30*unit,53*unit, 315*unit, 160*unit, 5) // roundRect是我百度查的一个绘制圆角矩形的方法
ctx.drawImage(that.data.chapterCover,30*unit,50*unit, 315*unit, 180*unit) // 填充封面图
ctx.setFontSize(14) // 设置文字的字体大小
ctx.setFillStyle('#333') // 文字的颜色
// ctx, name, nameWidth, maxWidth, startX, srartY, wordsHight
that.wordsWrap(ctx, title, 220, 290*unit, 50*unit, 240*unit, 16*unit); // wordsWrap也是百度查的,用来文字内容换行
that.wordsWrap(ctx, content.slice(0,120) + ' ...', 220, 260*unit, 50*unit, 280*unit, 16*unit) // 截取文字,超出范围拼接上 '...'
ctx.save() // 因为下面开始裁剪圆形,所以保存以上画好的内容
ctx.beginPath()
ctx.arc(185*unit, 365*unit, 25*unit, 0, 2 * Math.PI)//先画一个圆形
ctx.clip()//裁剪圆形
ctx.drawImage(res.tempFilePath,160*unit, 340*unit, 50*unit, 50*unit) // 填充用户头像
ctx.restore() // 恢复之前保存的绘图上下文
ctx.setFontSize(14)
ctx.setFillStyle('#333')
ctx.fillText(studyChapterNum + '节', 100*unit, 425*unit) //学习课程
ctx.fillText(`${hours}时${minutes}分`, 225*unit, 425*unit)// 学习时长
ctx.save() // 保存画好的内容
ctx.draw(function(){ // 文档:把当前画布指定区域的内容导出生成指定大小的图片。在 draw() 回调里调用该方法才能保证图片导出成功
wx.canvasToTempFilePath({
canvasId: 'shareFrends',
success(res) {
that.setData({
imgsrc: res.tempFilePath // 弹出分享图的html是写在当前页面的自定义子组件里的,所以涉及到组件传值,如果整个流程都是在一个页面内的,在此处拿到绘制的临时地址直接调用保存本地的API就好了
})
}
})
})
}
})
},
// 以下是用户点击分享按钮时执行的
jumpInvite:function(e){
var that = this
wx.showLoading({
title: '正在生成分享图',
})
if (this.data.imgsrc == '' || this.data.imgsrc == undefined) { // 防止没拿到canvas绘制需要好的临时地址
this.createCanvas()
}
setTimeout(() => {
wx.hideLoading()
that.invite.showInv();//显示自定义子组件弹框
that.setData({
finishShare: true // 控制分享图html展示
})
}, 2000);
}
},
// 以下代码是写在自定义子组件里的
// 点击朋友圈时执行
_openImage:function(){
this.setData({
imgShow: true, // 展示代替canvas的html
inviteCard: false,
// 以下父组件传来的参数
imgsrc: this.properties.imgsrc,
pyqInfo: this.properties.pyqInfo,
chapterCover: app.globalData.vediohost + this.properties.pyqInfo.chapterCover
})
},
_saveImage: function (e) {
var cansurl = this.properties.imgsrc // 父组件传来的canvas画的图片临时地址
var that = this
wx.saveImageToPhotosAlbum({
filePath: cansurl,
success(res) {
console.log(res)
wx.showModal({
content: '图片已保存到相册,赶紧晒一下吧~',
showCancel: false,
confirmText: '好的',
confirmColor: '#333',
success: function (res) {
if (res.confirm) {
console.log('用户点击确定');
that.setData({
imgShow: false // 隐藏代替canvas的html
})
}
}
})
},
fail() {
that.savePoster(e)
}
})
},
// 相册授权
savePoster: function (e) {
var that = this;
//获取相册授权
wx.getSetting({
success(res) {
if (!res.authSetting['scope.writePhotosAlbum']) {
wx.authorize({
scope: 'scope.writePhotosAlbum',
success() { //这里是用户同意授权后的回调
// wx.showLoading({
// title: "图片生成中"
// })
that._saveImage(e)
},
fail() { //这里是用户拒绝授权后的回调
that.setData({
authorizeShow: true
})
}
})
} else { //用户没有授权
that._saveImage(e)
}
}
})
},
// 以上完成整个流程,要根据自己项目的需求自行改动
注意1:这里文档中说了,wx.downloadFile需要的是https的请求,所以有时调用downloadFile下载不成功的原因之一是因为你可能用了http导致的
注意2: canvas的标签节点样式不可以使用display: none; 此类隐藏节点的样式,否则你最后保存到相册是一个空白图
解决办法太多了,比如给canvas标签,position: fixed;,top: -999rpx;
注意3:程序员每天都会面临未知的问题,当遇到问题之后一定要先分析整个操作的流程,整理好思路之后再着手写代码,遇到坑之后先自己思考下为什么,再去百度或者请教其他大佬,养成独立思考的习惯有助于成长哦!