今天把文章详情页最后一个按钮「生成海报」的功能开发完了, 这里介绍下实现思路和具体开发流程
交代些背景
其实在最早之前的小程序中已经实现了一次, 具体可以参考利用云开发优化博客小程序(三)-- 生成海报功能, 主要还是使用原生的 cavans 进行组装, 原本想代码 copy 过来改改就行了, 但总觉得原来的代码写的不是特别好.
于是想看看是否有现成的轮子可以利用, 果然发现了 wxa-plugin-canvas 这款组件, 通过非常简单的配置就可以生成精美的海报.
小程序使用 NPM
在总结生成海报功能之前还是有必要记录下小程序 NPM 的使用, 避免一些不必要的坑.
考虑到小程序本身的大小限制, 使用 NPM 的方式是最佳的.
原因是根据官方文档介绍, 小程序 NPM 包里只有构建文件生成目录会被算入小程序包的占用空间, 上传小程序代码时也只会上传该目录的代码. 这样大大减少了上传的代码体积.
下面简单介绍下小程序端如何使用 NPM 的「其实根据官方文档按照步骤就可以了」.
以我目前小程序的路径为例, 在 / miniprogram 新增文件夹 node_modules, 在命令行指向到 / miniprogram 目录下:
image
通过命令进行安装:
NPM install wxa-plugin-canvas --production
安装成功后, 即可在小程序开发工具中进行构建, 构建前需要勾选使用 NPM 模块
image
然后点击开发者工具中的菜单栏: 工具 --> 构建 NPM 即可:
image
构建完成后会生成 miniprogram_npm 目录, 到这里, 项目端基本就调通了.
image
wxa-plugin-canvas
在构建完之后, 就可以正常使用 wxa-plugin-canvas 这个自定义组件, 使用方式还是比较简单的.
首先在你需要的页面引入该组件:
- {
- "usingComponents": {"poster": "wxa-plugin-canvas/poster"}
- }
然后就可以在 wsml 中使用了:
<poster id="poster" hide-loading="{{false}}" preload="{{false}}" config="{{posterConfig}}" bind:success="onPosterSuccess" bind:fail="onPosterFail"></poster>
由于我们在生成海报前, 需要异步获取一些用于海报的数据, 所以我们采用异步生成的海报方式.
需要引入该组件的 poster/poster.JS 文件, 然后在代码中调用即可:
import Poster from '../../utils/poster'; Page({ /** * 异步生成海报 */ onCreatePoster() { // setData 配置数据 this.setData({ posterConfig: {...} }, () => { Poster.create(); }); } })
核心代码解析
海报需要的数据
先来看看分享海报的整体结构:
image
首先需要确认海报的构成需要哪些数据, 在调用组件前先获取好相应的数据.
在我设计的海报中主要包含三块内容, 用户的信息 (头像和昵称), 文章信息(首图, 标题, 简介) 和最重要的文章的小程序码.
用户信息和文章信息其实比较简单, 在小程序的详情页两者数据都有, 但这里有两个问题点需要注意下.
第一个是域名问题, 在画布中使用到的图片都需要配置域名, 头像的域名和公众号文章首图的域名
https://mmbiz.qpic.cn https://wx.qlogo.cn
image
第二个是公众号首图的问题, 公众号素材列表返回的图片 url 其实是 http 的, 但小程序规定绑定的域名必须是 https 的, 当时比较无奈, 后来尝试改用 https 访问首图的 url 也可以, 不幸中的万幸, 所以在使用首图地址时进行替换下:
imageUrl = imageUrl.replace('http://', 'https://')
最后就是文章的小程序码了, 需要利用小程序的 getUnlimited 的 API, 具体可以参考官方文档, 目前已经提供了云调用的方式「无需获取 access_token」, 调用起来还是比较简单的.
原本打算在文章同步的时候「adminService」直接生成对应文章的小程序码, 代码写完后本地调试可以, 但上传至云端后测试发现一直报错, 逛了轮胎才知道原来不支持, 同时触发器也不支持云调用, 所以这个计划泡汤了, 我在代码中打了 TODO.
image
既然这样, 那就在生成海报的时候进行生成, 同时生成后直接上传至云存储, 将对应的 FileID 保存至文章集合中, 这样只用生成一次就可以一直使用了, 具体代码如下:
/** * 新增文章二维码 * @param {} event */ async function addPostQrCode(event) { let scene = 'timestamp=' + event.timestamp; let result = await cloud.openapi.wxacode.getUnlimited({ scene: scene, page: 'pages/detail/detail' }) if (result.errCode === 0) { let upload = await cloud.uploadFile({ cloudPath: event.postId + '.png', fileContent: result.buffer, }) await db.collection("mini_posts").doc(event.postId).update({ data: { qrCode: upload.fileID } }); let fileList = [upload.fileID] let resultUrl = await cloud.getTempFileURL({ fileList, }) return resultUrl.fileList } return [] }
但这里有个尴尬的地方是, 生成小程序码的 API 中的 scene 参数最大长度是 32, 而文章 id 的长度已经是 32 了, 无法根据文章 id 进行拼接跳转页面的路径了, 所以这里暂时用了 mini_posts 集合中 timestamp 字段「理论上也是唯一的」.
所以在详情页中也需要兼容 timestamp 这个字段.
海报图片展示
海报图片展示就比较简单了, 使用个弹窗, 将生成好的海报图片进行展示即可:
/** * 生成海报成功 - 回调 * @param {} e */ onPosterSuccess(e) { const { detail } = e; this.setData({ posterImageUrl: detail, isShowPosterModal: true }) console.info(detail) },
保存海报图片
保存图片使用 wx.saveImageToPhotosAlbum 调用用户相册, 这里主要需要兼容用户拒绝相册授权的一些列操作, 具体代码如下:
/** * 保存海报图片 */ savePosterImage: function () { let that = this wx.saveImageToPhotosAlbum({ filePath: that.data.posterImageUrl, success(result) { console.log(result) wx.showModal({ title: '提示', content: '二维码海报已存入手机相册, 赶快分享到朋友圈吧', showCancel: false, success: function (res) { that.setData({ isShowPosterModal: false, isShow: false }) } }) }, fail: function (err) { console.log(err); if (err.errMsg === "saveImageToPhotosAlbum:fail auth deny") { console.log("再次发起授权"); wx.showModal({ title: '用户未授权', content: '如需保存海报图片到相册, 需获取授权. 是否在授权管理中选中" 保存到相册 "?', showCancel: true, success: function (res) { if (res.confirm) { console.log('用户点击确定') wx.openSetting({ success: function success(res) { console.log('打开设置', res.authSetting); wx.openSetting({ success(settingdata) { console.log(settingdata) if (settingdata.authSetting['scope.writePhotosAlbum']) { console.log('获取保存到相册权限成功'); } else { console.log('获取保存到相册权限失败'); } } }) } }); } } }) } } }); },
总结
有好的开源组件可以充分利用, 避免重复造轮子, 有机会也可以学习下别人的实现方式.
多看看文档, 其实小程序的文档真的挺详细的.
来源: http://www.jianshu.com/p/2ac59b1f9509