需求

公司开发微信小程序,有一个海报页面,需要用户点击生成海报,可以将该该swipe-item 生成一个带二维码的图片,最终由纯前端实现!


技术调研

*
因为小程序的打包限制,不可能将所有的图片都放在代码目录里面。所以就得使用远端的图片,但是因为canvas对跨域图片的不支持,可以使用微信小程序中的API
:wx.getImageInfo()现将远端的图片临时保存。
* canvas 组件是由客户端创建的原生组件,它的层级是最高的,不能通过 z-index 控制层级;并且使用display:none 和
visibility:hidden
的方式隐藏会导致canvas无法捕获画布内容,所以通过给canvas外部嵌套一个width:0;height:0;overflow:hidden的父级。
实现步骤

将远端图片暂存本地(以用户头像为例)
wx.getUserInfo({ //获取微信用户信息 success: function (res) { this
.getImageInfo(res.userInfo.avatarUrl);// 调取图片处理方法 this.setData({ userName:
res.userInfo.nickName }); } }); getImageInfo(url){ // 图片缓存本地的方法 if(typeof url
==='string'){ wx.getImageInfo({ // 小程序获取图片信息API src: url, success: function
(res) { this.setData({ head_img:res.path }) }, fail(err) { console.log(err) }
}) }
canvas绘制图片

点击生成海报按钮
handlePoster(e){ wx.getSetting({ // 获取用户设置 success(res) { if (!res.authSetting[
'scope.writePhotosAlbum']) { // 如果用户之前拒绝了授权 wx.openSetting({ success(tag){ if
(tag.authSetting["scope.writePhotosAlbum"]){ // 用户在设置页选择同意授权 // wx.showLoading({
// title: '正在生成...', // }) } } }); }else{ // 用户已经授权 wx.showLoading({ title:
'正在生成...', }) } } })
此时页面出现loading状态,canvas开始绘制海报:

首先绘制整个底部背景


在这里,我暂且用本地图片作为演示代替。绘制的canvas图片大小为750px*1344px大小,最终生成格式为jpg。大小大概就200多kb。非常的小,清晰度也不错。

我选择的背景图如上图所示;
const ctx = wx.createCanvasContext('notes'); ctx.clearRect(0, 0, 0, 0); const
arr2 = ['./big_img1.png', this.data.nodesData[index].detail_pic]; //
有图片海报背景图&&海报正文图片 const WIDTH=750; const HEIGHT=1334; // 绘制图片模板的 底图
ctx.drawImage(arr2[0], 0, 0, WIDTH, HEIGHT);
绘制海报的正文图片
// 绘制图片模板的 banner图 ctx.drawImage(arr2[1], 40,40, 670, 580);
绘制时间

因为我们的海报上面的时间 xxxx.xx.xx中的每个数字和点都是图片,所以时间也是一个个的小的以数字命名的图片绘制上去。而后台提供给我们的时间是个
秒(s)为单位的时间戳。首先得将时间戳转换为一个时间数字组成的数组:
dateFilter(date){ let gmt = new Date(date * 1000); let
nYear=String(gmt.getFullYear()); let nMonth =this.numFormatting(gmt.getMonth()+1
); let nDay =this.numFormatting(gmt.getDate()); let end_date = (nYear + '.' +
nMonth +'.' + nDay).split("") ; return end_date }, /** * 处理 月份 天数 个位数 */
numFormatting(num){if(String(num).length<2){ return '0'+num }else{ return num }
},
假设处理好的时间格式为 :
let times=['2', '0', '1', '8', '.', '0', '4','.','1','2'];
接下来是绘制时间:
// 绘制 图片模板 的时间 const TEXT_DATE = ['2', '0', '1', '8', '.', '0', '4','.','1','2'
];for (let i = 0; i < TEXT_DATE.length; i++) { if (TEXT_DATE[i] != '.') { var
path3 = `./${TEXT_DATE[i]}.png`; }else { var path3 = './point.png'; } let
clientx =40 + 16 * i; ctx.drawImage(path3, clientx, 640, 16, 32); }
绘制用户头像

因为二维码中显示的用户头像是一个圆形图像,所以处理方式为:
// 绘制头像 ctx.save(); let r=32; let d = r*2; let cx = 102; let cy = 1172; ctx.arc
(cx+r, cy+r, r,0, 2 * Math.PI); ctx.clip(); ctx.drawImage(this.data.head_img,
cx, cy, d, d); ctx.restore();
这个时候 ,海报所需要的所有图片已经绘制成功,接下来我们开始处理文字部分:

canvas 绘制文字

canvas并不会自动的将文字折行处理,所以我们得自己考虑每行显示多少个文字,还得考虑中英文和符号所占字节的不同,在此,封装一个处理方法:
textByteLength(text,num){ // text为传入的文本 num为单行显示的字节长度 let strLength = 0; //
text byte length let rows=1; let str=0; let arr=[]; for (let j = 0; j <
text.length; j++) {if (text.charCodeAt(j) > 255) { strLength += 2; if
(strLength > rows * num) { strLength++; arr.push(text.slice(str, j)); str = j;
rows++; } }else { strLength++; if (strLength > rows * num) {
arr.push(text.slice(str, j)); str = j; rows++; } } } arr.push(text.slice(str,
text.length));return [strLength, arr, rows] // [处理文字的总字节长度,每行显示内容的数组,行数] },
这个时候 就可以开始绘制文字了:

绘制正文和 出处
const CONTENT_ROW_LENGTH = 40; // 正文 单行显示字符长度 let [contentLeng, contentArray,
contentRows] =this.textByteLength(this.data.nodesData[index].text,
CONTENT_ROW_LENGTH); ctx.setTextAlign('left') ctx.setFontSize(32); let
contentHh =32 * 1.3; for (let m = 0; m < contentArray.length; m++) {
ctx.fillText(contentArray[m],40, 732 + contentHh * m); } // 绘制 出处
ctx.setTextAlign('right') ctx.setFontSize(32); ctx.fillText(`——${this
.data.nodesData[index].refer}`,710, 996, 710);
绘制二维码边上的说明
// 绘制二维码右边说明 ctx.setTextAlign('left') ctx.setFontSize(28); ctx.setFillStyle(
'rgba(34,34,34,.64)') ctx.fillText('长按小程序码', 250, 1174); ctx.fillText(`${this
.data.userName}邀你进入掌阅读好书`,250, 1230); ctx.draw();
此时如果canvas标签能够显示查看,就能看到一个完美的海报绘制完成,现在只差最后一步,保存canvas;使用微信方法
canvasToTempFilePath()将canvas海报保存到本地临时文件路径;在使用saveImageToPhotosAlbum()将图片保存到本地相册:
setTimeout(function () { wx.canvasToTempFilePath({ canvasId: 'notes', fileType:
'jpg', success: function (res) { wx.saveImageToPhotosAlbum({ filePath:
res.tempFilePath, success(res) { wx.hideLoading(); wx.showToast({ title:'保存成功',
}); }, fail(){ wx.hideLoading() } }) } }) },500);
然后查看存储图片的路径文件夹,最终生成的海报:



总结:


在做这个功能之前,canvas用的非常少,很多方法都是现成百度查询得出来的结果,这次的canvas生成海报并保存本地的实现过程中,让我学到了很多东西;因为项目做得比较急,很多的代码没有经过细致的打磨,在此,也希望看到本文章的朋友们及时指点。

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信