手把手带你在小程序中实现保存图片组件功能

本篇文章带大家聊聊微信小程序保存图片组件开发,希望对大家有所帮助! 许多微信小程序通过保存海报让用户去分享活动让更多的人知道自己的小程序,想必在平时开发小程序的时候应该有遇见过吧。...

本篇文章带大家聊聊微信小程序保存图片组件开发,希望对大家有所帮助!

手把手带你在小程序中实现保存图片组件功能插图1

许多微信小程序通过保存海报让用户去分享活动让更多的人知道自己的小程序,想必在平时开发小程序的时候应该有遇见过吧。

今天我就来分享下之前在公司做的一个小程序保存海报的功能。首先我先描述下之前在公司做的需求是什么样的。公司上线的小程序会有一个长期的活动目的就是去推广新用户,每个用户都要有一张属于自己的海报,通过个人海报去推广则只是单纯的一种方式。

接到任务后,我也先去万能互联网做了调查但是我的师兄和我说这个做过类似的但是当时只是单纯为了完成任务所以代码很乱,然后他就从其他项目的代码找呀找,然后找到了给我~~~ 而当时给到我的时间紧任务重呀只好先用着调整一些并且交差了。之后呢我就根据网上的文章然后一步一步踩坑,一步一步走实现了一个保存海报的组件。

思路

首先声明下组件采用的是uniapp,具体实现了可以绘制图片、绘制文字以及保存海报至相册的基本功能,在开发中这些也完全够用了。

通过canvas绘制海报。通过uni.canvasToTempFilePath将绘制好的 canvas转为图片。通过uni.saveImageToPhotosAlbum将本地临时路径的图片保存至手机相册中。而我的想法是将所有采用的方法全部封装到组件中,只通过父组件去调用需要使用的方法和调整相关的参数即可。具体使用可以查看示例代码

通过canvas绘制海报内容的顺序先后问题

通过使用promise对象决定绘制海报内容的顺序先后。promise.all()方法进行canvas最后一步的绘画操作context.draw()

注意uni.getImageInfo()

在绘制图片 和 头像时,组件通过uni.getImageInfo()去获取图片的相关信息,调用该方法成功的前提是需要在微信小程序后台配置download域名和request域名当然最好把uploadFile域名也一起配置,防止出差错。但是官方给出的提示是配置download域名白名单即可,但是获取不到图片信息,这算是一个大坑了。

如果没有进行相关配置,在调试时 或者 体验版 正式版等 打开了vconsole调试工具。uni.getImageInfo() 是可以获取到图片信息的,一旦关闭了vconsole uni.getImageInfo() 将会fail, 也是个坑。

本组件方法,变量介绍

props

canvasInfo Object (必需)

canvasWidth 画布宽度

canvasHeight 画布高度

canvasId 画布标识

isFullScreen Boolean

为ture时表示画布为手机屏幕全屏,canvasInfo设置的宽高将失效。

默认为 false

methods

canvasInit(callback) canvas初始化,所有有关画布canvas操作需在其回调函数操作。

drawCanvasImage(context, src, _imageWidth, _imageHeight, dx, dy) 在canvas绘制一张图片

drawCircularAvatar(context, url, _circularX, _circularY, _circularR) 在canvas绘制一张圆形图片

drawText(options) 在canvas绘制单行、多行文本

startDrawToImage(context, promiseArr, callback) 将canvas操作draw()进行绘制

posterToPhotosAlbum(filePath) 保存至手机相册

示例代码

<template><view><viewclass="savePosterItem"><imagev-show="tempFilePath":src="tempFilePath"></image><save-poster-comv-show="!tempFilePath"ref="savePoster":canvasInfo="canvasInfo"></save-poster-com></view><buttonclass="savePosterBtn"type="primary"@click="saveBtnFun">保存海报</button></view></template><script>importSavePosterComfrom'@/components/SavePosterCom/SavePosterCom.vue'exportdefault{components:{SavePosterCom},data(){return{canvasInfo:{canvasWidth:620,canvasHeight:950,canvasId:'save-poster'},tempFilePath:'',canvasBgUrl:'https://images.pexels.com/photos/4065617/pexels-photo-4065617.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',avatarUrl:'https://p9-passport.byteacctimg.com/img/user-avatar/4dbf31fa6dec9c65b78a70d28d843c04~300x300.image'}},onLoad(){let{drawCanvasImage,drawCircularAvatar,drawText}=this.$refs.savePoster.$options.methodsthis.$refs.savePoster.canvasInit(({context,comThis})=>{//获取画布宽高letcanvasWH=comThis.canvasWH//绘制海报背景图letpromise_1=drawCanvasImage(context,this.canvasBgUrl,canvasWH.canvasWidth,canvasWH.canvasHeight)//必须先绘制玩海报背景图再去操作其他绘制内容promise_1.then(res=>{letpromise_2=drawCircularAvatar(context,this.avatarUrl,canvasWH.canvasWidth/2,canvasWH.canvasHeight/7,70)letpromise_3=drawText({context:context,text:'皮皮虾仁',dx:(canvasWH.canvasWidth/2)+60,dy:canvasWH.canvasHeight/4,fontSize:30,fontColor:'#5D4037'})letpromise_4=drawCanvasImage(context,this.avatarUrl,150,150,(canvasWH.canvasWidth/2)+85,(canvasWH.canvasHeight-165))this.$refs.savePoster.startDrawToImage(context,[promise_1,promise_2,promise_4],(tempFilePath)=>{this.tempFilePath=tempFilePath})})})},methods:{saveBtnFun(){uni.showModal({title:'保存海报',content:'海报将被保存至相册中',confirmText:'保存',success:(res)=>{if(res.confirm){this.$refs.savePoster.posterToPhotosAlbum(this.tempFilePath)}}})}}}</script><style>.savePosterItem{text-align:center;}.savePosterItem>image{width:620rpx;height:950rpx;}.savePosterBtn{margin-top:40rpx;width:80%;}</style>

组件源码

<template><view><canvas:canvas-id="canvasInfo.canvasId":style="{width:canvasWH.canvasWidth+'px',height:canvasWH.canvasHeight+'px'}"></canvas></view></template><script>exportdefault{name:'savePosterCom',data(){return{userPhoneWHInfo:{},canvasWH:{canvasWidth:0,canvasHeight:0}}},props:{//决定保存下来的图片的宽高canvasInfo:{type:Object,default:()=>{return{canvasWidth:0,canvasHeight:0,canvasId:'canvasId'}}},//canvas画布是不是全屏,默认是false。false时使用必须传canvasInfoisFullScreen:Boolean},created(){this.userPhoneWHInfo=this.getPhoneSystemInfo()if(this.isFullScreen){//画布全屏this.canvasWH.canvasWidth=this.userPhoneWHInfo.windowWidththis.canvasWH.canvasHeight=this.userPhoneWHInfo.windowHeight}else{//指定宽高this.canvasWH.canvasWidth=this.canvasInfo.canvasWidththis.canvasWH.canvasHeight=this.canvasInfo.canvasHeight}},mounted(){},methods:{/***获取用户手机屏幕信息*/getPhoneSystemInfo(){constres=uni.getSystemInfoSync();return{windowWidth:res.windowWidth,windowHeight:res.windowHeight}},/**获取CanvasContext实例*@param{String}canvasId*/getCanvasContextInit(canvasId){returnuni.createCanvasContext(canvasId,this)},/**保存海报组件初始化*@param{Function}callback(context)回调函数*/canvasInit(callback){letcontext=this.getCanvasContextInit(this.canvasInfo.canvasId)if(context){callback({context:context,comThis:this})}},/**将上诉的绘制画到画布中并且将画布导出为图片*@paramcontext画布*@param{Promise[]}存放Promise的数组*@param{Function}callback保存图片后执行的回调函数(本地图片临时路径)*/startDrawToImage(context,promiseArr,callback){//将之前在绘图上下文中的描述(路径、变形、样式)画到canvas中letcanvasId=this.canvasInfo.canvasIdlettempFilePath=''Promise.all(promiseArr).then(res=>{context.draw(false,async()=>{callback(awaitthis.canvasToImage(canvasId))})})},/***在canvas绘制一张图片*@paramcontext画布*@paramsrc图片资源*@param_imageWidth图片宽度*@param_imageHeight图片高度*/drawCanvasImage(context,src,_imageWidth,_imageHeight,dx,dy){returnnewPromise((resolve,reject)=>{uni.getImageInfo({src:src,success:res=>{context.drawImage(res.path,(dx-_imageWidth),(dy-_imageHeight),_imageWidth,_imageHeight)resolve(context)},})})},/**绘制一个圆形头像*@paramcontext画布*@paramurl图片地址*@param_circularX圆心X坐标*@param_circularY圆心Y坐标*@param_circularR圆半径*/drawCircularAvatar(context,url,_circularX,_circularY,_circularR){letdx=_circularX-_circularR;letdy=_circularY-_circularR;letdwidth=_circularR*2;letdheight=_circularR*2returnnewPromise((resolve,reject)=>{uni.downloadFile({url:url,success:res=>{context.save()context.beginPath()//_circularX圆的x坐标_circularY圆的y坐标_circularR圆的半径context.arc(_circularX,_circularY,_circularR,0,2*Math.PI)context.clip()//dx:图像的左上角在目标canvas上X轴的位置//dy:图像的左上角在目标canvas上Y轴的位置//dwidth:在目标画布上绘制图像的宽度,允许对绘制的图像进行缩放//dheight:在目标画布上绘制图像的高度,允许对绘制的图像进行缩放context.drawImage(res.tempFilePath,dx,dy,dwidth,dheight)context.restore()//context.draw()resolve(context)}})})},/**绘制多行文本注:,和空格都算一个字*@paramcontext画布*@paramtext需要被绘制的文本*@paramdx左上角x坐标*@paramdy右上角y坐标*@paramrowStrnum每行多少个字(默认为text字体个数->单行)*@paramfontSize文字大小(默认16)*@paramfontColor文字颜色(默认black)*@paramlineHeight单行文本行高(默认0)*/drawText(options){let{context,text,dx,dy,rowStrnum=text.length,lineHeight=0,fontSize=16,fontColor='black'}=optionsreturnnewPromise((resolve,reject)=>{context.setFontSize(fontSize)context.setFillStyle(fontColor)context.setTextBaseline('middle')//获取需要绘制的文本宽度lettextWidth=Number(context.measureText(text).width)//console.log('textWidth',textWidth)//获取文本的字数lettextNum=text.length//获取行数向上取整letlineNum=Math.ceil(textNum/rowStrnum)//console.log('textNum',textNum)//console.log('lineNum',lineNum)for(leti=0;i<lineNum;i++){letsliceText=text.slice(i*rowStrnum,(i+1)*rowStrnum)//fillText的dx=文字最左边的距离到屏幕政策的距离context.fillText(sliceText,dx-textWidth,dy+i*lineHeight);}resolve(context)})},/**将画布导出为图片*@paramcanvasId画布标识*/canvasToImage(canvasId){returnnewPromise((resolve,reject)=>{uni.canvasToTempFilePath({canvasId:canvasId,//画布标识success:res=>{//在H5平台下,tempFilePath为base64resolve(res.tempFilePath)},fail:err=>{console.log('err',err)reject(err)}},this)})},/**保存生成的图片到本地相册中*@param{String}filePath图片临时路劲*/posterToPhotosAlbum(filePath){console.log('filePath',filePath)uni.showLoading({title:'保存中...'})uni.saveImageToPhotosAlbum({filePath:filePath,success:(res)=>{uni.showToast({title:'保存成功,请前往手机相册中查看',mask:true,icon:'none',duration:2000})},fail:(err)=>{console.log('err',err)if(err.errMsg.includes('deny')||err.errMsg.includes('denied')){//用户选择拒绝this.openSetting()}elseif(err.errMsg.includes('failcancel')){//用户在保存图片时取消了uni.showToast({title:'已取消保存,无法保存至相册',mask:true,icon:'none',duration:2000})return}},complete:()=>{uni.hideLoading()}})},/***打开摄像头设置权限页面*/openSetting(){uni.showModal({title:'温馨提示',content:'保存图片至相册中,需要您同意添加访问相册权限',cancelText:'拒绝',confirmText:'同意',success:res=>{if(res.confirm){uni.openSetting({success:settingdata=>{if(settingdata.authSetting['scope.writePhotosAlbum']){console.log('获取权限成功,给出再次点击图片保存到相册的提示。')uni.showToast({title:'授权成功,请再次点击保存',icon:'none',duration:2000,})}else{console.log('获取权限失败,给出不给权限就无法正常使用的提示')uni.showToast({title:'需要访问相册权限',icon:'none',duration:2000,})}},fail:(res)=>{console.log('err',err)}})}else{uni.showToast({title:'已拒绝授权,无法保存至相册',mask:true,icon:'none',duration:2000})return}}})}}}</script><style></style>

效果

手把手带你在小程序中实现保存图片组件功能插图3

产品猿社区致力收录更多优质的商业产品,给服务商以及软件采购客户提供更多优质的软件产品,帮助开发者变现来实现多方共赢;

日常运营的过程中我们难免会遇到各种版权纠纷等问题,如果您在社区内发现有您的产品未经您授权而被用户提供下载或使用,您可按照我们投诉流程处理,点我投诉

本文来自用户发布投稿,不代表产品猿立场 ;若对此文有疑问或内容有严重错误,可联系平台客服反馈;

部分产品是用户投稿,可能本文没有提供官方下下载地址或教程,若您看到的内容没有下载入口,您可以在我们产品园商城搜索看开发者是否有发布商品;若您是开发者,也诚邀您入驻商城平台发布的产品,地址:点我进入

如若转载,请注明出处:http://www.chanpinyuan.cn/43022.html;
(0)
上一篇 2023年5月19日 下午4:16
下一篇 2023年5月19日 下午4:16

相关推荐

发表回复

登录后才能评论
分享本页
返回顶部