100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 小程序生成海报分享朋友圈

小程序生成海报分享朋友圈

时间:2022-11-06 01:00:46

相关推荐

小程序生成海报分享朋友圈

需求

利用微信强大的社交能力通过小程序达到裂变的目的,拉取新用户。生成的海报如下

需求分析

1、利用小程序官方提供的api可以直接分享转发到微信群打开小程序2、利用小程序生成海报保存图片到相册分享到朋友圈,用户长按识别二维码关注公众号或者打开小程序来达到裂变的目的

实现方案

一、分析如何实现

相信大家应该都会有类似的迷惑,就是如何按照产品设计的那样绘制成海报,其实当时我也是不知道如何下手,认真想了下得通过canvas绘制成图片,这样用户保存这个图片到相册,就可以分享到朋友圈了。但是要绘制的图片上面不仅有文字还有数字、图片、二维码等且都是活的,这个要怎么动态生成呢。认真想了下,需要一点一点的将文字和数字,背景图绘制到画布上去,这样通过api最终合成一个图片导出到手机相册中。

二、需要解决的问题

1、二维码的动态获取和绘制(包括如何生成小程序二维码、公众号二维码、打开网页二维码)2、背景图如何绘制,获取图片信息3、将绘制完成的图片保存到本地相册4、处理用户是否取消授权保存到相册

三、实现步骤

这里我具体写下围绕上面所提出的问题,描述大概实现的过程

①首先创建canvas画布,我把画布定位设成负的,是为了不让它显示在页面上,是因为我尝试把canvas通过判断条件动态的显示和隐藏,在绘制的时候会出现问题,所以采用了这种方法,这里还有一定要设置画布的大小。

<canvascanvas-id="myCanvas"style="width:690px;height:1085px;position:fixed;top:-10000px;"></canvas>

②创建好画布之后,先绘制背景图,因为背景图我是放在本地,所以获取<canvas>组件 canvas-id 属性,通过createCanvasContext创建canvas的绘图上下文 CanvasContext 对象。使用drawImage绘制图像到画布,第一个参数是图片的本地地址,后面两个参数是图像相对画布左上角位置的x轴和y轴,最后两个参数是设置图像的宽高。

constctx=wx.createCanvasContext("myCanvas")ctx.drawImage("/img/study/shareimg.png",0,0,690,1085)

③创建好背景图后,在背景图上绘制头像,文字和数字。通过getImageInfo获取头像的信息,这里需要注意下在获取的网络图片要先配置download域名才能生效,具体在小程序后台设置里配置。

获取头像地址,首先量取头像在画布中的大小,和x轴Y轴的坐标,这里的result[0]是我用promise封装返回的一个图片地址

letheadImg=newPromise(function(resolve){wx.getImageInfo({src:`${app.globalData.baseUrl2}${that.>`,success:function(res){resolve(res.path)},fail:function(err){console.log(err)wx.showToast({title:"网络错误请重试",icon:"loading"})}})})letavatarurl_width=60,//绘制的头像宽度avatarurl_heigth=60,//绘制的头像高度avatarurl_x=28,//绘制的头像在画布上的位置avatarurl_y=36;//绘制的头像在画布上的位置ctx.save();//先保存状态已便于画完圆再用ctx.beginPath();//开始绘制//先画个圆前两个参数确定了圆心(x,y)坐标第三个参数是圆的半径四参数是绘图方向默认是false,即顺时针ctx.arc(avatarurl_width/2+avatarurl_x,avatarurl_heigth/2+avatarurl_y,avatarurl_width/2,0,Math.PI*2,false);ctx.clip();//画了圆再剪切原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内ctx.drawImage(result[0],avatarurl_x,avatarurl_y,avatarurl_width,avatarurl_heigth);//推进去图片

这里举个例子说下如何绘制文字,比如我要绘制如下这个“字”,需要动态获取前面字数的总宽度,这样才能设置“字”的x轴坐标,这里我本来是想通过measureText来测量字体的宽度,但是在iOS端第一次获取的宽度值不对,关于这个问题,我还在微信开发者社区提了bug,所以我想用另一个方法来实现,就是先获取正常情况下一个字的宽度值,然后乘以总字数就获得了总宽度,亲试是可以的。

letallReading=97/6/app.globalData.ratio*wordNumber.toString().length+325;ctx.font="normalnormal30pxsans-serif";ctx.setFillStyle("#ffffff")ctx.fillText("字",allReading,150);

④绘制公众号二维码,和获取头像是一样的,也是先通过接口返回图片网络地址,然后再通过getImageInfo获取公众号二维码图片信息

⑤如何绘制小程序码,具体官网文档也给出生成无限小程序码接口,通过生成的小程序可以打开任意一个小程序页面,并且二维码永久有效,具体调用哪个小程序二维码接口有不同的应用场景,具体可以看下官方文档怎么说的,也就是说前端通过传递参数调取后端接口返回的小程序码,然后绘制在画布上(和上面写的绘制头像和公众号二维码一样的)

ctx.drawImage("小程序码的本地地址",x轴,Y轴,宽,高)

⑥最终绘制完把canvas画布转成图片并返回图片地址

wx.canvasToTempFilePath({canvasId:"myCanvas",success:function(res){canvasToTempFilePath=res.tempFilePath//返回的图片地址保存到一个全局变量里that.setData({showShareImg:true})wx.showToast({title:"绘制成功",})},fail:function(){wx.showToast({title:"绘制失败",})},complete:function(){wx.hideLoading()wx.hideToast()}})

⑦保存到系统相册;先判断用户是否开启用户授权相册,处理不同情况下的结果。比如用户如果按照正常逻辑授权是没问题的,但是有的用户如果点击了取消授权该如何处理,如果不处理会出现一定的问题。所以当用户点击取消授权之后,来个弹框提示,当它再次点击的时候,主动跳到设置引导用户去开启授权,从而达到保存到相册分享朋友圈的目的。

//获取用户是否开启用户授权相册if(!openStatus){wx.openSetting({success:(result)=>{if(result){if(result.authSetting["scope.writePhotosAlbum"]===true){openStatus=true;wx.saveImageToPhotosAlbum({filePath:canvasToTempFilePath,success(){that.setData({showShareImg:false})wx.showToast({title:"图片保存成功,快去分享到朋友圈吧~",icon:"none",duration:2000})},fail(){wx.showToast({title:"保存失败",icon:"none"})}})}}},fail:()=>{},complete:()=>{}});}else{wx.getSetting({success(res){//如果没有则获取授权if(!res.authSetting["scope.writePhotosAlbum"]){wx.authorize({scope:"scope.writePhotosAlbum",success(){openStatus=truewx.saveImageToPhotosAlbum({filePath:canvasToTempFilePath,success(){that.setData({showShareImg:false})wx.showToast({title:"图片保存成功,快去分享到朋友圈吧~",icon:"none",duration:2000})},fail(){wx.showToast({title:"保存失败",icon:"none"})}})},fail(){//如果用户拒绝过或没有授权,则再次打开授权窗口openStatus=falseconsole.log("请设置允许访问相册")wx.showToast({title:"请设置允许访问相册",icon:"none"})}})}else{//有则直接保存openStatus=truewx.saveImageToPhotosAlbum({filePath:canvasToTempFilePath,success(){that.setData({showShareImg:false})wx.showToast({title:"图片保存成功,快去分享到朋友圈吧~",icon:"none",duration:2000})},fail(){wx.showToast({title:"保存失败",icon:"none"})}})}},fail(err){console.log(err)}})}

总结

至此所有的步骤都已实现,在绘制的时候会遇到一些异步请求后台返回的数据,所以我用promise和async和await进行了封装,确保导出的图片信息是完整的。在绘制的过程确实遇到一些坑的地方。比如初开始导出的图片比例大小不对,还有用measureText测量文字宽度不对,多次绘制(可能受网络原因)有时导出的图片上的文字颜色会有误差等。如果你也遇到一些比较坑的地方可以一起探讨下做个记录,下面附下完整的代码

importregeneratorRuntimefrom"../../utils/runtime.js"//引入模块constapp=getApp(),api=require("../../service/http.js");varctx=null,//创建canvas对象canvasToTempFilePath=null,//保存最终生成的导出的图片地址openStatus=true;//声明一个全局变量判断是否授权保存到相册//获取微信公众号二维码getCode:function(){returnnewPromise(function(resolve,reject){api.fetch("/wechat/open/getQRCodeNormal","GET").then(res=>{console.log(res,"获取微信公众号二维码")if(res.code==200){console.log(res.content,"codeUrl")resolve(res.content)}}).catch(err=>{console.log(err)})})},//生成海报asynccreateCanvasImage(){letthat=this;//点击生成海报数据埋点that.setData({generateId:"点击生成海报"})if(!ctx){letcodeUrl=awaitthat.getCode()wx.showLoading({title:"绘制中..."})letcode=newPromise(function(resolve){wx.getImageInfo({src:codeUrl,success:function(res){resolve(res.path)},fail:function(err){console.log(err)wx.showToast({title:"网络错误请重试",icon:"loading"})}})})letheadImg=newPromise(function(resolve){wx.getImageInfo({src:`${app.globalData.baseUrl2}${that.>`,success:function(res){resolve(res.path)},fail:function(err){console.log(err)wx.showToast({title:"网络错误请重试",icon:"loading"})}})})Promise.all([headImg,code]).then(function(result){constctx=wx.createCanvasContext("myCanvas")console.log(ctx,app.globalData.ratio,"ctx")letcanvasWidthPx=690*app.globalData.ratio,canvasHeightPx=1085*app.globalData.ratio,avatarurl_width=60,//绘制的头像宽度avatarurl_heigth=60,//绘制的头像高度avatarurl_x=28,//绘制的头像在画布上的位置avatarurl_y=36,//绘制的头像在画布上的位置codeurl_width=80,//绘制的二维码宽度codeurl_heigth=80,//绘制的二维码高度codeurl_x=588,//绘制的二维码在画布上的位置codeurl_y=984,//绘制的二维码在画布上的位置wordNumber=that.data.wordNumber,//获取总阅读字数//nameWidth=ctx.measureText(that.data.wordNumber).width,//获取总阅读字数的宽度//allReading=((nameWidth+375)-325)*2+380;//allReading=nameWidth/app.globalData.ratio+325;allReading=97/6/app.globalData.ratio*wordNumber.toString().length+325;console.log(wordNumber,wordNumber.toString().length,allReading,"获取总阅读字数的宽度")ctx.drawImage("/img/study/shareimg.png",0,0,690,1085)ctx.save();//先保存状态已便于画完圆再用ctx.beginPath();//开始绘制//先画个圆前两个参数确定了圆心(x,y)坐标第三个参数是圆的半径四参数是绘图方向默认是false,即顺时针ctx.arc(avatarurl_width/2+avatarurl_x,avatarurl_heigth/2+avatarurl_y,avatarurl_width/2,0,Math.PI*2,false);ctx.clip();//画了圆再剪切原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内ctx.drawImage(result[0],avatarurl_x,avatarurl_y,avatarurl_width,avatarurl_heigth);//推进去图片ctx.restore();//恢复之前保存的绘图上下文状态可以继续绘制ctx.setFillStyle("#ffffff");//文字颜色ctx.setFontSize(28);//文字字号ctx.fillText(that.data.currentChildren.name,103,78);//绘制文字ctx.font="normalbold44pxsans-serif";ctx.setFillStyle("#ffffff");//文字颜色ctx.fillText(wordNumber,325,153);//绘制文字ctx.font="normalnormal30pxsans-serif";ctx.setFillStyle("#ffffff")ctx.fillText("字",allReading,150);ctx.font="normalnormal24pxsans-serif";ctx.setFillStyle("#ffffff");//文字颜色ctx.fillText("打败了全国",26,190);//绘制文字ctx.font="normalnormal24pxsans-serif";ctx.setFillStyle("#faed15");//文字颜色ctx.fillText(that.data.percent,154,190);//绘制孩子百分比ctx.font="normalnormal24pxsans-serif";ctx.setFillStyle("#ffffff");//文字颜色ctx.fillText("的小朋友",205,190);//绘制孩子百分比ctx.font="normalbold32pxsans-serif";ctx.setFillStyle("#333333");//文字颜色ctx.fillText(that.data.singIn,50,290);//签到天数ctx.fillText(that.data.reading,280,290);//阅读时长ctx.fillText(that.data.reading,508,290);//听书时长//书籍阅读结构ctx.font="normalnormal28pxsans-serif";ctx.setFillStyle("#ffffff");//文字颜色ctx.fillText(that.data.bookInfo[0].count,260,510);ctx.fillText(that.data.bookInfo[1].count,420,532);ctx.fillText(that.data.bookInfo[2].count,520,594);ctx.fillText(that.data.bookInfo[3].count,515,710);ctx.fillText(that.data.bookInfo[4].count,492,828);ctx.fillText(that.data.bookInfo[5].count,348,858);ctx.fillText(that.data.bookInfo[6].count,212,828);ctx.fillText(that.data.bookInfo[7].count,148,726);ctx.fillText(that.data.bookInfo[8].count,158,600);ctx.font="normalnormal18pxsans-serif";ctx.setFillStyle("#ffffff");//文字颜色ctx.fillText(that.data.bookInfo[0].name,232,530);ctx.fillText(that.data.bookInfo[1].name,394,552);ctx.fillText(that.data.bookInfo[2].name,496,614);ctx.fillText(that.data.bookInfo[3].name,490,730);ctx.fillText(that.data.bookInfo[4].name,466,850);ctx.fillText(that.data.bookInfo[5].name,323,878);ctx.fillText(that.data.bookInfo[6].name,184,850);ctx.fillText(that.data.bookInfo[7].name,117,746);ctx.fillText(that.data.bookInfo[8].name,130,621);ctx.drawImage(result[1],codeurl_x,codeurl_y,codeurl_width,codeurl_heigth);//绘制头像ctx.draw(false,function(){//canvas画布转成图片并返回图片地址wx.canvasToTempFilePath({canvasId:"myCanvas",success:function(res){canvasToTempFilePath=res.tempFilePaththat.setData({showShareImg:true})console.log(res.tempFilePath,"canvasToTempFilePath")wx.showToast({title:"绘制成功",})},fail:function(){wx.showToast({title:"绘制失败",})},complete:function(){wx.hideLoading()wx.hideToast()}})})})}},//保存到系统相册saveShareImg:function(){letthat=this;//数据埋点点击保存学情海报that.setData({saveId:"保存学情海报"})//获取用户是否开启用户授权相册if(!openStatus){wx.openSetting({success:(result)=>{if(result){if(result.authSetting["scope.writePhotosAlbum"]===true){openStatus=true;wx.saveImageToPhotosAlbum({filePath:canvasToTempFilePath,success(){that.setData({showShareImg:false})wx.showToast({title:"图片保存成功,快去分享到朋友圈吧~",icon:"none",duration:2000})},fail(){wx.showToast({title:"保存失败",icon:"none"})}})}}},fail:()=>{},complete:()=>{}});}else{wx.getSetting({success(res){//如果没有则获取授权if(!res.authSetting["scope.writePhotosAlbum"]){wx.authorize({scope:"scope.writePhotosAlbum",success(){openStatus=truewx.saveImageToPhotosAlbum({filePath:canvasToTempFilePath,success(){that.setData({showShareImg:false})wx.showToast({title:"图片保存成功,快去分享到朋友圈吧~",icon:"none",duration:2000})},fail(){wx.showToast({title:"保存失败",icon:"none"})}})},fail(){//如果用户拒绝过或没有授权,则再次打开授权窗口openStatus=falseconsole.log("请设置允许访问相册")wx.showToast({title:"请设置允许访问相册",icon:"none"})}})}else{//有则直接保存openStatus=truewx.saveImageToPhotosAlbum({filePath:canvasToTempFilePath,success(){that.setData({showShareImg:false})wx.showToast({title:"图片保存成功,快去分享到朋友圈吧~",icon:"none",duration:2000})},fail(){wx.showToast({title:"保存失败",icon:"none"})}})}},fail(err){console.log(err)}})}},

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。