近日接觸到微信小程序canvas繪制海報(bào)移稳,經(jīng)過(guò)一番折騰犀勒,算是能保存了屎飘,記錄一下吧。
新版本Api接口
<!-- canvas.wxml -->
<!-- width,height是根據(jù)設(shè)計(jì)圖及rpx與px像素比計(jì)算出來(lái)的寬高-->
<!-- wx.getSystemInfo({
success: (res) => {
if(res.model.indexOf("iPhone X") > -1){
this.globalData.distance = 36
}
this.globalData.rpx = res.windowWidth / 750
this.globalData.width = 635 * (res.windowWidth / 750)
this.globalData.height = 974 * (res.windowWidth / 750)
},
})-->
<canvas type="2d" id="myCan" style="width: {{width}}px;height:{{height}}px"></canvas>
生成海報(bào)
saveImg(res) {
const index = res.detail
wx.showLoading({
title: '海報(bào)生成中'
})
// 新接口 canvas2d
query.select('#myCan')
.fields({
node: true,
size: true
})
.exec((res) => {
console.log(res[0].node)
const canvas = res[0].node
const ctx = canvas.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = res[0].width * dpr
canvas.height = res[0].height * dpr
ctx.scale(dpr, dpr)
saveCanvers(canvas, ctx, this.data.images[index], this.data.postersInfo.title, this.data.postersInfo.desc, this.data.postersInfo.desc1, this.data.avatar, '入秋的番薯', this.data.miniCode)
})
},
方法封裝
async function saveCanvers(canvas, ctx, headImg, title, desc, desc1, codeImg) {
const App = getApp()
let rpx = App.globalData.rpx
// 海報(bào)解釋下面向上移動(dòng)的距離 如果兩行解釋 那么不上移 如果一行解釋 那么上移35
let upMove = desc1 ? 0 : 35
// 背景色
ctx.fillStyle = '#ffffff'
// canvas大小
ctx.fillRect(0, 0, 638 * rpx, (974 - upMove) * rpx)
// 繪制主圖
await downLoadDrow(canvas, ctx, headImg, 0, 0, 638 * rpx, 558 * rpx)
// 標(biāo)題
ctx.font = 36 * rpx + 'px'
ctx.fillStyle = '#373B3E'
ctx.fillText(title, 30 * rpx, 608 * rpx, 589 * rpx)
// 解釋
ctx.font = 24 * rpx + 'px'
ctx.fillStyle = '#9D9D9D'
ctx.fillText(desc, 30 * rpx, 648 * rpx, 589 * rpx)
desc1 && ctx.fillText(desc1, 30 * rpx, 684 * rpx, 589 * rpx)
// 虛線
ctx.strokeStyle = '#DEDEDE'
ctx.setLineDash([3, 3]);
ctx.beginPath();
ctx.moveTo(30 * rpx, (708 - upMove) * rpx);
ctx.lineTo(610 * rpx, (708 - upMove) * rpx);
ctx.stroke();
// 圓形頭像
ctx.save()
ctx.beginPath()
ctx.arc(72 * rpx, (808 - upMove) * rpx, 42 * rpx, 0, 2 * Math.PI)
// 保證原型填充無(wú)bug
ctx.fill()
ctx.clip()
await downLoadDrow(canvas, ctx, userInfo.avatarUrl, 30 * rpx, (766 - upMove) * rpx, 84 * rpx, 84 * rpx)
ctx.restore()
// 用戶昵稱
ctx.font = 32 * rpx + 'px'
ctx.fillStyle = '#000000'
ctx.fillText(userInfo.nickName, 134 * rpx, (813 - upMove) * rpx, 200 * rpx)
// 邀請(qǐng)掃碼
ctx.font = 24 * rpx + 'px'
ctx.fillText('邀您掃碼體驗(yàn)', 30 * rpx, (888 - upMove) * rpx, 200 * rpx)
// 房車小程序
ctx.fillText('上汽大通原廠房車小程序', 30 * rpx, (930 - upMove) * rpx, 300 * rpx)
// 小程序碼
await downLoadDrow(canvas, ctx, codeImg, 400 * rpx, (738 - upMove) * rpx, 206 * rpx, 206 * rpx)
setTimeout(() => {
wx.canvasToTempFilePath({
canvas: canvas,
success: (can) => {
wx.getSetting({
success(res) {
if (!res.authSetting['scope.writePhotosAlbum']) { //判斷權(quán)限
wx.authorize({ //獲取權(quán)限
scope: 'scope.writePhotosAlbum',
success() {
saveImg(can.tempFilePath)
}
})
} else {
saveImg(can.tempFilePath)
}
}
})
},
fail: (err) => {
wx.showToast({
title: '保存失敗贾费,請(qǐng)稍后重試',
icon: 'none'
})
return false
}
})
}, 500)
}
function downLoadDrow(canvas, ctx, url, x, y, w, h) {
return new Promise((resolve, reject) => {
wx.getImageInfo({
src: url,
success: (res) => {
const img = canvas.createImage();
img.src = res.path; //微信請(qǐng)求返回頭像
img.onload = () => {
console.log(img)
ctx.drawImage(img, x, y, w, h);
img.src = ""
resolve()
}
},
fail: (err) => {
wx.hideLoading()
wx.showToast({
title: '下載圖片失敗钦购,請(qǐng)稍后重試',
icon: 'none'
})
reject()
return false
}
})
})
}
function saveImg(path) {
wx.hideLoading()
wx.saveImageToPhotosAlbum({
filePath: path,
success: (res) => {
wx.showToast({
title: '已保存到相冊(cè)',
icon: 'success',
duration: 2000
})
},
fail: (err) => {
wx.showToast({
title: '保存失敗',
icon: 'none'
});
}
})
}
老是在繪制的時(shí)候閃退,內(nèi)存溢出褂萧,也不知道怎么解決押桃,網(wǎng)上找了點(diǎn)方法,但好像都不行导犹,也可能沒(méi)找到重點(diǎn)唱凯,不得已羡忘,再來(lái)一套老版本canvas
老版本canvas
<canvas canvas-id="myCan" style="width: {{width}}px;height:{{height}}px"></canvas>
生成圖片
saveImg(res) {
// 組件里面?zhèn)鬟f的圖片索引 不在細(xì)說(shuō) 可忽略
const index = res.detail
wx.showLoading({
title: '海報(bào)生成中'
})
let ctx = wx.createCanvasContext("myCan")
saveCanversOld(ctx, this.data.postersInfo.swiper[index], this.data.postersInfo.title, this.data.postersInfo.desc, this.data.postersInfo.desc1, this.data.postersInfo.buffer)
},
方法封裝
async function saveCanversOld(ctx, headImg, title, desc, desc1, codeImg) {
let userInfo = wx.getStorageSync('userInfo')
const App = getApp()
let rpx = App.globalData.rpx
let upMove = desc1 ? 0 : 35
// 背景色
ctx.setFillStyle('#ffffff')
// canvas大小
ctx.fillRect(0, 0, 638 * rpx, (974 - upMove) * rpx)
// 繪制主圖
await downLoadDrowOld(ctx, headImg, 0, 0, 638 * rpx, 558 * rpx, 'headImg')
// 標(biāo)題
ctx.setFontSize(36 * rpx)
ctx.setFillStyle('#373B3E')
ctx.fillText(title, 30 * rpx, 608 * rpx, 589 * rpx)
// 解釋
ctx.setFontSize(24 * rpx)
ctx.setFillStyle('#9D9D9D')
ctx.fillText(desc, 30 * rpx, 648 * rpx, 589 * rpx)
desc1 && ctx.fillText(desc1, 30 * rpx, 684 * rpx, 589 * rpx)
// 虛線
ctx.setStrokeStyle('#DEDEDE')
ctx.setLineDash([6, 5]);
ctx.beginPath();
ctx.moveTo(30 * rpx, (708 - upMove) * rpx);
ctx.lineTo(610 * rpx, (708 - upMove) * rpx);
ctx.stroke();
// 圓形頭像
ctx.save()
ctx.beginPath()
ctx.arc(72 * rpx, (808 - upMove) * rpx, 42 * rpx, 0, 2 * Math.PI)
// 保證原型填充無(wú)bug
ctx.fill()
ctx.clip()
await downLoadDrowOld(ctx, userInfo && userInfo.avatarUrl || config.defaultImg, 30 * rpx, (766 - upMove) * rpx, 84 * rpx, 84 * rpx)
ctx.restore()
// 用戶昵稱
ctx.setFontSize(32 * rpx)
ctx.setFillStyle('#000000')
ctx.fillText(userInfo && userInfo.nickName || config.defaultUser, 134 * rpx, (813 - upMove) * rpx, 200 * rpx)
// 邀請(qǐng)掃碼
ctx.setFontSize(24 * rpx)
ctx.fillText('邀您掃碼體驗(yàn)', 30 * rpx, (888 - upMove) * rpx, 200 * rpx)
// 房車小程序
ctx.fillText('上汽大通原廠房車小程序', 30 * rpx, (930 - upMove) * rpx, 300 * rpx)
// 小程序碼
await downLoadDrowOld(ctx, codeImg, 400 * rpx, (738 - upMove) * rpx, 206 * rpx, 206 * rpx, 'miniCode')
ctx.draw(false, () => {
wx.canvasToTempFilePath({
fileType: 'jpg',
canvasId: 'myCan',
success: (res) => {
console.log(res.tempFilePath)
saveImg(res.tempFilePath)
},
fail: (err) => {
console.log(err)
}
})
})
}
// 下載圖片
function downLoadDrowOld(ctx, url, x, y, w, h, type = '') {
return new Promise((resolve, reject) => {
if (type == 'miniCode') {
//聲明文件系統(tǒng)
const fs = wx.getFileSystemManager();
//隨機(jī)定義路徑名稱
var times = new Date().getTime();
var codeimg = wx.env.USER_DATA_PATH + '/' + times + '.png';
//將base64圖片寫入
fs.writeFile({
filePath: codeimg,
data: url,
encoding: 'base64',
success: (res) => {
//寫入成功了的話,新的圖片路徑就能用了
ctx.drawImage(codeimg, x, y, w, h)
resolve()
// 繪制成功之后要釋放 內(nèi)存
setTimeout(() => {
fs.unlink({
filePath: codeimg,
success: (res) => {
console.log('清除文件成功', res)
},
err: (err) => {
wx.hideLoading()
wx.showToast({
title: '釋放文件內(nèi)存失敗',
icon: 'none'
})
}
})
}, 2000)
}
});
} else if (type == 'headImg') {
// 獲取圖片信息進(jìn)行截取 截取的起止位置也需要計(jì)算 然后繪制 x, y, w, h只代表繪制到canvas上的范圍
wx.getImageInfo({
src: url,
success: (res) => {
// 根據(jù)ui算出來(lái)的要展示的比例 width / height
const rat = 1.143
// 圖片的寬度
const imgWidth = res.width
// 圖片的高度
const imgHeight = res.height
// 剪切圖片的寬度磕昼, 高度卷雕, 剪切X起始位置, 剪切Y起始位置
let cutWidth, cutHeight, cutStartX, cutStartY
if (imgWidth <= imgHeight) {
cutWidth = imgWidth
cutHeight = imgWidth / rat
cutStartX = 0
cutStartY = (imgHeight - cutHeight) / 2
} else {
cutHeight = imgHeight
cutWidth = imgHeight * rat
cutStartX = (imgWidth - cutWidth) / 2
cutStartY = 0
}
ctx.drawImage(res.path, cutStartX, cutStartY, cutWidth, cutHeight, x, y, w, h)
resolve()
},
fail: (err) => {
console.log(err)
wx.hideLoading()
wx.showToast({
title: '獲取圖片信息失敗',
icon: 'none'
})
reject()
return false
}
})
} else {
wx.downloadFile({
url,
success: (res) => {
ctx.drawImage(res.tempFilePath, x, y, w, h)
resolve()
},
fail: (err) => {
wx.hideLoading()
wx.showToast({
title: '下載圖片失敗票从,請(qǐng)稍后重試',
icon: 'none'
})
reject()
return false
}
})
}
})
}
function saveImg(path) {
wx.hideLoading()
wx.saveImageToPhotosAlbum({
filePath: path,
success: (res) => {
wx.showToast({
title: '已保存到相冊(cè)',
icon: 'success',
duration: 2000
})
},
fail: (err) => {
wx.showToast({
title: '保存失敗',
icon: 'none'
});
}
})
}
里面有一些buffer文件的轉(zhuǎn)換漫雕,繪制圖片時(shí)比例大小的裁剪,分享圖的后臺(tái)配置愈合與獲取等峰鄙,都是比較常規(guī)的業(yè)務(wù)浸间,就不記了。