小程序制作海报,支持一键生成多张

1. 前言

生成海报是小程序一项寻常普遍的低成本推广方式,在小程序中通过引导用户生成带有小程序二维码的海报发上票圈,来吸引更多的流量。

 

2. 需求分析

在与朋友圈类似的 带有文字描述 和 最多带有9张图片的 列表中,实现

1/ 使用云开发,生成二维码

2/ 点击图片,会根据图片的标签绘制对应模板的海报

3/ 将生成的海报图片进行预览,并提供“保存到本地", 增加用户拒绝授权保存图片到本地相册后的处理

4/ 增加了一键保存的功能:即一键将某个人发的多张朋友圈图片,按照顺序绘制成海报图片保存到本地, 并实时显示绘制进度 

 

3. 实现步骤

3.1  抽象封装canvas绘制组件

弱鸡的我一开始是drawImg, fillText, drawRect 一个接口一个接口分别去绘制图像,文字,矩形,但是扛不住需求变更的速度啊。在改了n个样式之后,卑微的我终于!!!决定把canvas绘制的逻辑抽取出来。

canvas绘制,当然离不开canvas页面标签,所以我们不如把它封装成一个组件 canvasdrawer,这个组件从外部接收一个Json,当接收到新的json时,开始绘制,json中每个item添加一个type,标志它是图像 / 文字 / 矩形,然后调用相应的封装好的绘制方法。

需要注意的是, 当绘制的图像是网路图片时,需要先拿到图片的临时路径才能来进行绘制。当海报参数中存在多张网络图片时,使用Promise.all 异步获取图片临时路径。

绘制完之后,调用 canvasToTempFile 生成临时图片链接,我们可以 triggerEvent 通知父组件该海报已绘制完成,并返回生成的海报的图片临时链接,用于实现预览或者本地保存

 

3.1.1 使用监听observer监听传入的json 是否改变,若改变了再进行绘制

properties: {// 传入的jsonpainting: {type: Object,value: {view: []},// 监听传入的json 对象是否发生改变,决定是否需要重新绘制observer(newVal, oldVal) {// 判断当前有没有在进行绘制的对象if (!this.data.isPainting) {// 将对象转换成字符串再进行比较if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {// 宽、高为必须的if (newVal && newVal.width && newVal.height) {this.setData({showCanvas: true,isPainting: true})this.readyPigment()}} else {// 如果对象没有发生改变,但是模式又不是“same”模式的话,就出触发事件if (newVal && newVal.mode !== 'same') {this.triggerEvent('getImage', {errMsg: 'canvasdrawer:samme params'})}}}}}
},

 

3.1.2  获取图片列表

// 获取图片列表
getImagesInfo(views) {// 1.将获取到的每张图片信息推送到imagelist中const imageList = []for (let i = 0; i < views.length; i++) {if (views[i].type === 'image') {imageList.push(this.getImageInfo(views[i].url))}}// 2.临时图片下载,使用promise.all 异步下载,每8张一个异步taskconst loadTask = []for (let i = 0; i < Math.ceil(imageList.length / 8); i++) {loadTask.push(new Promise((resolve, reject) => {Promise.all(imageList.splice(i * 8, 8)).then(res => {resolve(res)}).catch(res => {reject(res)})}))}// promise.all返回的res为多个promise返回的数组拼接成的数组,有所有的task返回Promise.all(loadTask).then(res => {let tempFileList = []for (let i = 0; i < res.length; i++) {tempFileList = tempFileList.concat(res[i])}this.setData({tempFileList})this.startPainting()})
},

 

3.1.3 若是网络图片,先拿到图片的临时链接

// 获取图片信息
getImageInfo(url) {return new Promise((resolve, reject) => {// 如果图片有缓存,则从缓存中读取if (this.cache[url]) {resolve(this.cache[url])} else {// 匹配图片链接地址是否匹配const objExp = new RegExp(/^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/)if (objExp.test(url)) {wx.getImageInfo({src: url,complete: res => {if (res.errMsg === 'getImageInfo:ok') {this.cache[url] = res.pathresolve(res.path)} else {this.triggerEvent('getImage', {errMsg: 'canvasdrawer:download fail'})reject(new Error('getImageInfo fail'))}}})} else {this.cache[url] = urlresolve(url)}}})
},

 

3.1.4 绘制完成后,使用wx.canvasToTempFilePath 将canvas转化成临时图片链接,并通知给父级组件

// 将图片保存到本地
saveImageToLocal() {const {width,height} = this.datawx.canvasToTempFilePath({x: 0,y: 0,width,height,canvasId: this.data.canvasId,complete: res => {if (res.errMsg === 'canvasToTempFilePath:ok') {this.setData({showCanvas: false,isPainting: false,tempFileList: []})this.triggerEvent('getImage', {tempFilePath: res.tempFilePath,errMsg: 'canvasdrawer:ok'})} else {this.triggerEvent('getImage', {errMsg: 'canvasdrawer:fail'})}}}, this)
}

 

3.2  获取海报参数

对一个有灵魂的海报来说 二维码 是必不可少的,其中,二维码的生成可通过两种方式实现:

1. 需要后台的协助:传输给后台相应的参数,由后台调官方接口去生成二维码,再返回给前端

2. 前端自给自足: 小程序官方增加了云开发,我们可以通过新增云函数中,在云函数中传入参数,生成二维码

注意:生成的二维码是buffer data,需要将其转化为图片链接


/* 获取云开发的小程序码 */
const getQrCode = (path, scene, dataType) => {return new Promise((resolve, reject) => {let param = {page: path,scene: scene || '',width: 350}if (!path) {reject('must have path')return}if (scene && scene.length > 32) {reject('scene\' length must less than 32')return}console.log('小程序码~云参数:', param)wx.cloud.callFunction({name: 'getNewQrCode',data: param}).then(res => {let dataif (res.result.errCode == 0) {if (dataType == 'base64') {data = `data:image/jpeg;base64,${wx.arrayBufferToBase64(res.result.buffer)}`}if (dataType == 'buffer') {data = res.result.buffer}if (data) {resolve(data)} else {reject('unknow data type')}} else {reject(res.result.errMsg)}}).catch(err => {reject(err)})})
}

为了使小程序当前生命周期结束时,二维码仍然有效,我们可以调用小程序提供给我们的在用户存储目录写入文件的权限,将生成的二维码图片保存到用户存储目录中(wx.env.USER_DATA_PATH)

/* 图片临时缓存 */
const localQrImageUrl = (buffer) => {return new Promise((resolve, reject) => {const TMP_IMG_NAME = 'TMP_QR_CODE_';// 添加时间戳使之前生成的二维码图片不被覆盖const TIME_STAMP = new Date().getTime().toString().substr(-8);const filePath = `${wx.env.USER_DATA_PATH}/${TMP_IMG_NAME}${TIME_STAMP}.jpg`;// const filePath = `${wx.env.USER_DATA_PATH}/${TMP_IMG_NAME}.jpg`;// console.log('临时存储路径:', filePath)wx.getFileSystemManager().writeFile({filePath,data: buffer,encoding: 'binary',success() {// console.log('临时存储路径保存好了:', filePath)resolve(filePath)},fail() {wx.getFileSystemManager().rmdir({dirPath: `${wx.env.USER_DATA_PATH}/`,success() {wx.showModal({title: "温馨提示",content:"本地内存不足,已删除该小程序清除本地缓存,请再次点击生成海报"});reject(new Error("已清除本地缓存,请再次点击生成海报"));},fail() {console.log("删除本地缓存失败");// console.log('临时存储路径保存错了')reject(new Error("ERROR_BASE64SRC_WRITE"));}});}})})
}

 

3.3 动态改变海报模板的某些特定参数

比如说多个商品都用到了同一个海报模板,即图片,文字和形状的大小,位置都是一致的,但是图片的链接地址url, 文字的内容text 可能不一样,这个时候我们可以通过一个reqParams 请求参数去动态改变模板;

我们在定义海报模板时,给需要变更内容的字段添加上一个标识。然后在 reqParams 请求参数中携带 { 标识 : 更改后的值},即可实现变更。

/*** 动态改变海报模板的参数* 拼接示例:{width:, height:, desc:value}*/
changePosterParams() {let descList = Object.keys(this.reqParam);descList.forEach(desc=>{let newTarget = this.reqParam[desc];if(desc == 'width'){this.poster['width'] = newTarget;}else if(desc == 'height'){this.poster['height'] = newTarget;}else{let target = this.getItemByKey(this.poster['views'], desc);if(target && target.url){target.url = newTarget;}else if(target && target.content){target.content = newTarget;}}})
}/*** 在数组中获取到键desc的值为某一个值的对象* 注意:需确保键值只能唯一,否则匹配到一个最后一个匹配到的项,即后面的会覆盖前面的* @param {Array} arr * @param {String} keyValue */
getItemByKey(arr,keyValue){let targetItem=null;arr.forEach(item=>{if (item['desc'] == keyValue){targetItem=item;}});return targetItem;
}

 

3.4 保存图片到本地相册

当用户授权当然保存没问题,但是当用户拒绝授权怎么办嘞。

小程序现在升级了,再也不能静默再去获取授权了,需要由用户的点击操作才能调起设置页面,引导用户自己打开授权

需注意的是:当没有请求过授权时,是需要先去请求授权,设置页面才会出现该选项的(比如保存到本地相册的权限);

// 保存图片,有处理如果用户不授权的情况
Poster.prototype.trySaveImg = function(imgUrl, successCallback) {const _this = this;wx.getSetting({success(res){console.log("用户当前开启的权限情况", res.authSetting);// 拒绝过授权,则打开设置页面(设置界面只会出现小程序已经向用户请求过的权限,所以需先向用户请求一次)if(res.authSetting['scope.writePhotosAlbum'] === false){wx.openSetting({success(res){// 如果还是没有授权的话if(!res.authSetting['scope.writePhotosAlbum']){wx.showToast({title: '未开启保存权限',image: '/images/common/err_tip_icon.png',duration: 2000})}}});}// 没有请求过授权或者已授权else{if(successCallback){successCallback(imgUrl);}else{_this.saveImg(imgUrl);}}}})
}

当没有请求过授权或者已授权的情况下,就直接调起保存动作

 // 保存图片saveImg(imgUrl) {wx.saveImageToPhotosAlbum({filePath: imgUrl,success(res) {wx.showToast({title: '保存图片成功',image: '/images/common/icon_toast_success.jpg',duration: 2000})},fail(err){console.log(err)wx.showToast({title: '未开启保存权限',image: '/images/common/err_tip_icon.png',duration: 2000})}})
}

 

3.5 一键绘制多张海报

上面我在 3.1.4 步骤中提到生成成功后会通知父级组件,这里我们在父级组件中监听这个生成成功事件,并拿到生成的海报图片,可进行预览并保存在本地

// 当前海报绘制完成, 检测是否还有未完成绘制的海报
eventGetImage(event){const {tempFilePath,errMsg} = event.detail// 绘制成功if (errMsg === 'canvasdrawer:ok') {this.setData({shareImage: tempFilePath,isShowCanvas: false // 重新初始化canvas画报为false,等参数拼接完再去绘制下一张});this.saveImg(this.data.shareImage); // 保存该张海报图片} // 当前海报绘制失败,增加失败图片序号到失败队列中,并绘制开始下一张else{console.log(errMsg);this.data.activeItemIndex++;this.data.failItemIndexList.push(this.data.activeItemIndex);this.startPainting();}
},

检测还有没有未完成的绘制任务

// 检查是否还有绘制任务
if(_this.data.activeItemIndex < _this.data.totalItemsLen-1){_this.data.activeItemIndex++;_this.startPainting();
}
// 绘制最后一张完成
else{_this.setData({isDoingSaveAll: false})wx.hideLoading();// 判断有没有绘制失败的if(_this.data.failItemIndexList.length > 0){let failItemIndexListString = _this.data.failItemIndexList.join(',')wx.showToast({title: "第"+failItemIndexListString+"张下载失败",image: '/images/common/err_tip_icon.png',duration: 2000});}else{wx.showToast({title: "下载成功",image: '/images/common/icon_toast_success.jpg',duration: 2000});}
}

 

4. 总结

github仓库完整代码

如果文章对你有帮助,麻烦点赞哦,一起走花路吧~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/72186.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小程序使用canvas标签生成海报并保存图片

先说一下做功能前的感受&#xff0c;简直一脸懵逼&#xff0c;第一次用canvas&#xff0c;只知道是个画布&#xff0c;其余什么都不知道…琢磨了一天才画出来… 开始之前百度了很久&#xff0c;想看看别人怎么写的&#xff0c;但是目前网上基本上用的都是wx.createCanvasContex…

html一键生成海报,微海报在线制作一键生成方法

近期&#xff0c;很多朋友都在问建站小兔兔一个问题&#xff0c;那就是为什么你们的微海报在网站页面上找不到呢&#xff1f;我想用建站兔做个微海报&#xff0c;在哪里啊&#xff1f;喵喵~呵呵~你当然在页面上找不到制作的窗口了&#xff0c;因为我们的微海报在线制作一键生成…

【微信小程序-原生开发】实用教程20 - 生成海报(实战范例为生成活动海报,内含生成指定页面的小程序二维码,保存图片到手机,canvas 系列教程)

可在系列教程的基础上继续开发&#xff0c;也可以单独使用 【微信小程序-原生开发】系列教程 效果预览 代码实现 点击触发生成海报 在活动详情页&#xff0c;指定点击某图标/按钮&#xff0c;触发跳转到生成海报的页面 pages\components\party\detail\index.js getPoster() …

2020赚钱机会总结,拾元富另附10个副业赚钱必备的工具与平台,看看你到底错过了多少钱!

赚钱不努力&#xff0c;努力不赚钱&#xff01;你会发现一个现象&#xff0c;自己很辛苦也很努力地去工作&#xff0c;甚至连假节日都不敢休息&#xff0c;平时也很节俭&#xff0c;也没买什么奢侈品&#xff0c;每个月也都有存钱。但是就是不知道为什么&#xff0c;钱存得很慢…

单日直播GMV破亿,爆品热销628w+,8月榜单有哪些看点?

今年的“抖音818发现好物节”&#xff0c;再次引爆了用户们在夏日的消费热情。 截止8月18日24时&#xff0c;大促成交额破百万直播间达3894个&#xff0c;挂购物车短视频投稿量达1621条&#xff0c;交易额破百万品牌达5560个&#xff0c;泛商城交易额破百万单品个数达400。 而纵…

微信重磅更新,视频号狂放大招:直播连麦打赏美颜齐上线,新增巨大流量入口

本文转载自 新榜 12月23日晚&#xff0c;微信迎来重大改版&#xff01;在最新7.0.20版本的微信中&#xff0c;视频号大招不断&#xff0c;不仅上线了连麦功能&#xff0c;支持美颜瘦脸、打赏等功能。 此外&#xff0c;微信还给视频号开放了两个重大入口&#xff0c;包括微信个…

谷歌开放生成式AI Bard,百度文心一言《唐伯虎点秋香》出圈

雷递网 雷建平 3月22日 谷歌今日正式开放类ChatGPT产品Bard的访问。谷歌称&#xff0c;这是一项早期实验&#xff0c;可让用户与生成AI协作。 Bard的服务从美国和英国开始&#xff0c;随着时间的推移将扩展到更多国家和语言。 谷歌称&#xff0c;用户可使用Bard来提高工作效率、…

文心ERNIE 3.0 Zeus千亿参数大模型,一键生成“学术范儿”论文标题

文章整理自哔哩哔哩科技区UP主“同济子豪兄”&#xff08;张子豪&#xff09;的主题分享「文心雕龙&#xff0c;一键生成学术灵感」。 文心ERNIE 3.0 Zeus作为基于知识增强的千亿模型&#xff0c;在各类真实场景的生成准确性、流畅性、相关性上全面领先业界其他大模型&#xff…

飞浆AI studio人工智能课程学习(3)-在具体场景下优化Prompt

文章目录 在具体场景下优化Prompt营销场景办公效率场景日常生活场景海报背景图生成办公效率场景预设Prompt 生活场景中日常学习Prompt: 给写完的代码做文档 将优质Prompt模板化Prompt 1:Prompt 1:Prompt 2步骤文本过长而导致遗失信息的示例 步骤三:归纳输入情况&#xff0c;确定…

聊聊软件登录界面的设计与交互

前面说了一堆废话&#xff0c;想看代码的可直接看第二章。 版本记录 日期备注2020-06-13初稿 零、前言 这个登录界面提取自最近正在做的一个项目&#xff0c;此项目曾被我自豪地称为是公司数采软件的颜值担当&#xff0c;虽然这里面有不少夸大的成分&#xff0c;但也并非担不…

交互设计介绍

交互设计是一门需要结合用户体验和设计思维的复杂学科。作为Ul设计师&#xff0c;我们需要通过了解用户需求和市场趋势&#xff0c;以及深入了解产品的特点和功能&#xff0c;来设计出符合用户需求的交互方式&#xff0c;以提高产品的用户体验和用户满意度。以下是一些交互设计…

Midjourney助力交互设计师设计网站主页

Midjourney的一大核心优势是提供创意设计&#xff0c;这个功能也可以用在网站主页设计上&#xff0c;使用Midjourney prompt 应尽量简单&#xff0c;只需要以"web design for..." or "modern web design for..."开头即可 比如设计一个通用SAAS服务的初创企…

诚意满满的奉上2000套Axure原型图设计源文件UI UX交互设计案例

网络上的Axure教程看了很多&#xff0c;大多数都是讲软件如何操作&#xff0c;讲交互逻辑和用户体验的却寥寥无几&#xff0c;而实际项目中却非常重交互逻辑和用户体验。把这2000套原型文件一一拆解&#xff0c;慢慢分析作者的设计思路&#xff0c;会发现&#xff0c;互联网产品…

11个优秀的交互设计作品集

本文转自墨刀编译自Vanschneider的10 inspiring ux portfolios and why they work。 不管你做什么样的设计&#xff0c;一个在线交互设计作品集都是必备的。在大多数情况下&#xff0c;公司和客户根本不会考虑你是做什么工作的。虽然很多交互设计师可能认为自己的工作不能很好…

Websocket的基本认识、使用与封装

目录 一、Websocket是什么 二、Websocket的基本使用 使用介绍 第一步 第二步 第三步 第四步 常用API介绍 WebSocket(url[, protocols]) WebSocket.readyState WebSocket.send(data) WebSocket.close([code[, reason]]) WebSocket.bufferedAmount WebSocket.exten…

asp.net paypal信用卡支付功能

前段时间有个客户网站需要用到信用卡支付功能&#xff0c;客户有个Website Payments Pro的帐号&#xff0c;第一次做这东西花了很多时间在上面&#xff0c;已做好运行了一段日子&#xff0c; 现在总结一下&#xff1a; 1. 先注册一个paypal开发者帐号&#xff0c;这个主要用于s…

通义听悟诞生背后,AI大模型打响应用第一枪

配图来自Canva可画 2023年伊始&#xff0c;ChatGPT的爆火出圈&#xff0c;迅速引发了业界对于生成式AI应用的关注&#xff0c;AI大模型的竞争更是愈演愈烈。 作为参与其中的重要玩家&#xff0c;阿里云先是在4月11日举行的阿里云峰会上&#xff0c;推出了通义千问大模型。紧接…

开发私有chatGPT(二)openai前景展望

使用 OpenAI 的强大模型构建下一代应用程序。 GPT-3可以执行各种自然语言任务、Codex 可以将自然语言转换为代码的 &#xff0c;DALLE&#xff0c;用于创建和编辑原始图像。 DALLE 开发人员现在可以开始使用&#xff0c;已经有超过3万人已经在使用DALLE来扩展他们的创造力并加…

快给你的对象做一个微信公众号播报吧-java版

一、前期准备 1.注册微信测试公众号 使用微信扫码登录此网站https://mp.weixin.qq.com/debug/cgi-bin/sandbox?tsandbox/login就能得到一个微信公众号测试号 推送消息需要用到的信息 用户扫码关注得到用户的id 编辑消息模板 此步骤的模板id、用户微信号id、以及自己的appID、…

2013年10月微软MVP当选名单揭晓!

微软公司于2001年8月起开始在亚洲与各大主要的第三方网站上的微软技术相关论坛合作&#xff0c;微软称之为“亚洲社区支持”计划。 为了鼓励大家在论坛中更好地互相帮助&#xff0c;共同提高&#xff0c;微软在全亚洲的微软论坛参与者中评选出那些技术水平高&#xff0c;积…