个人案例
- 星爸爸等优惠券
主要是出让相关线下品牌门店的优惠券,比如盒马,肯德基,星巴克等等优惠券
免费得星巴克肯德基等各大品牌优惠券扫码体验
- 最佳实践丨在云函数内使用 Redis 扩展
什么时候应该使用 Redis?Redis 的适用场景包括但不仅限于: 计数器:因为 Redis 操作是原子性的,通过原子递增或递减来做高并发用户的数据计数,比如点赞数、收藏数、分享数、商品抢购时的库存量、商品文章总数、评论数量等;排行榜:Redis 支持集合和有序集合的数据结构,且运行在内存中,因此可以存储一些类似于排行榜的数据,比如最近、最热、点击率最高、活跃度最高、评论最多等等的文章、商品、用户等;哈希表:用户粉丝列表、用户点赞列表、用户收藏列表、用户关注列表等;自动排序:存储时间戳,随着时间的变化,按照用户关注用户的最新动态列表等自动排序;会话缓存:使用 Redis 进行会话缓存,将 web session 存放在 Redis 中;全页缓存 FPC:可以将服务端渲染结果的缓存在 Redis 中;记录用户操作信息:用户是否点赞、用户是否收藏、用户是否分享等。安装 Redis 拓展1、安装扩展打开腾讯云控制台,进入到环境详情页面,点击左侧的「扩展应用」,进入到扩展能力详情页,并点击 Redis 拓展,安装拓展。 [图片] 2、创建 Redis 实例倘若安装中没有实例(即还没有购买 Redis 数据库,点击新建实例),倘若已经有实例的可以跳过,进入下一步。 [图片] 购买 Redis 数据库,创建实例,配备好私有网络。 [图片] 创建好实例后回到扩展选择刚刚创建(或者已有的)的实例: [图片] 点击完成创建: [图片] 看到有如下扩展即安装成功: [图片] 3、获取 Redis 信息创建好后查看拓展相关信息(在这里面我们便可以看到一起创建好的云函数啦): [图片] 在云函数中使用 Redis云函数内可以通过 Redis 客户端连接和操作 Redis 实例,推荐使用。 1、安装依赖首先进入到 Redis 的云函数目录中,然后执行命令 [代码]npm init -y[代码]初始化一个配置文件。 随后,执行 [代码]npm install --save redis[代码] 来安装相应的依赖。 安装完成后,云函数目录下将会出现 package.json 文件,内容类似以下: { "name": "redis", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "redis": "^3.0.2" } } 2、调用 Redis接下来可以在代码中调用 Redis 数据库了。 由于使用了云开发的 Redis 拓展,系统运行环境中会自动带上相应的配置,你可以直接使用相应的环境变量来链接 Redis 数据库。 'use strict'; const redis = require('redis') let client = redis.createClient({ host: process.env.HOST, port: process.env.PORT, // 需要填写真实的密码 password: 'xxx' }) exports.main = async (event, context, callback) => { let res = await new Promise((resolve, reject) => { client.get('test', function (err, reply) { if (err) { resolve({ err }) } resolve({ data: reply.toString() }) }) }) return { res } } 产品介绍云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为开发者提供高可用、自动弹性扩缩的后端云服务,包含计算、存储、托管等serverless化能力,可用于云端一体化开发多种端应用(小程序,公众号,Web 应用,Flutter 客户端等),帮助开发者统一构建和管理后端服务和云资源,避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。 开通云开发:https://console.cloud.tencent.com/tcb?tdl_anchor=techsite 产品文档:https://cloud.tencent.com/product/tcb?from=12763 技术文档:https://cloudbase.net?from=10004 技术交流群、最新资讯关注微信公众号【腾讯云云开发】
2021-05-11 - [致富经]开发小程序月入100万,小程序如何让Ta逆袭的?
首先声明:本人与被曝光之人无任何利益关系,彼此不认识、没有任何经济关联。所收集资料均为网络公开资料。 最初注意到这个小程序黑产开发者,是看到了一个帖子:《小程序上线19日,日广告收入稳定在3000 +》,这让我很好奇,我们都知道流量是需要获取,获取流量是需要成本的,这位开发者身上一定有值得我们去学习的。但当我仔细查阅其小程序及运营手段后,令我震惊,这完全就是黑产,涉及的金额之大已经足以判刑。 1、以砍价手法吸引流量,拒绝发货直接退款。 其首次试水这种操作手法的小程序叫“铺子优品”,就是利用各种电子产品,如iPhone、小米;口号化妆品,如Dior、YSL等,以多少次砍价免费拿或低价拿吸引消费者进行转发砍价,转发砍价完成后以“无货”等理由拒绝发货并强制退款。消费者已经在多个平台进行曝光,目前这个小程序已经被微信官方封禁。投诉链接 [图片] [图片] [图片] 2、部分发货的,发的是假冒伪劣产品。 其中一小部分,例如YSL,直接发假到不能再假的仿品给消费者,消费者砍完价后付的钱可能还能买数根这样的假口红。如果第一条行为只是涉嫌不诚信,那么这一条行为,直接触犯“销售假冒伪劣产品罪”、“诈骗罪”。这样的行为,已经是属于黑产而非灰产。曝光链接 [图片] <以上两条,就是这名“开发者”的所作所为> 但是今天我想说的远不止这些,更让我震惊的是这位本能闷声赚大钱的骗子,竟然做出了以下不可思议的举动—— 1、贼喊捉贼,上官方社区说自己很冤,还妄图申诉小程序。 这名小骗子,在欺诈了数千甚至上万名消费者,遭到消费者大量投诉后,竟然到了官方社区,发帖《日流量3万人遇到用户恶意投诉该如何解决?@所有开发者》,不仅贼喊捉贼,还带上了@所有开发者,我想说的是,正因为你的这篇帖子,我今天才要扒你,我相信绝大多数的小程序开发者不屑与你为伍,你这种人渣,就应该去监狱里蹲着。 [图片] 2、开始出售教程,建立黑产产业链,意图扩散传播。 在官方明确拒绝申诉后,这名小骗子开始在官方社区发帖子《电商小程序月入50万》、《小程序上线19日,日广告收入稳定在3000+》,意图通过高额的利润驱动更多底层开发者去找他交学费、买课程,让这种毒瘤黑产蔓延整个小程序。现在暂不知道有多少人交了学费,开始了一样的行为,在此我还是郑重警告这些人——小程序都是实名的,进去是分分钟的! [图片] <以上为小骗子的迷之操作> 根据我的观察,这名小骗子很明显不懂法律,在屡屡犯罪后竟然还敢堂而皇之的出现,当然官方在迅速封禁其首个小程序后,也许也没想到这个人竟然敢继续大张旗鼓的推进。目前据测算,这个人从腾讯拿走的流量主分成应该在大几十万元,加上假冒伪劣产品收的电商支付金额,应该是超过百万元。在此我希望,微信官方以此人为反面教材,出动腾讯法务予以报案、立案,保障消费者权益,净化小程序环境。 我整理了小骗子目前使用的公司主体与新的小程序信息: 公司主体:贵州火马科技有限公司 目前仍在运营的类似小程序:19严选、十元铺、是心选(更多小程序官方请查询主体信息) 希望得到微信官方的重视,小程序的生态健康关乎着每一位开发者。 -------------------------------------------------------------- 另附上其社区主页:查看这个小骗子的社区主页 相关举报内容通过搜索引擎搜索“铺子优品”即可查看。 题外话:这个小骗子还是很聪明的,铺子优品舆论爆炸后,使用的小程序名称以大牌傍名为主,比如19严选的搜索引擎,出来的基本都是网易严选,这样更多人就发现不了其作恶的真相了。 -------------------------------更新于0714-------------------------------- 在阿拉丁小程序看到了其新小程序19严选和十元铺的卖量广告,日UV都是万起步的。此贴我会每日更新,记录诈骗生活~ [图片]
2020-07-14 - 如何利用小程序提高10倍活动效果?
前言 大家好,我是一名程序员,从2017年开通了个技术公众号。 从2017年开启了一个长期的活动但是效果很一般,并且是越来越差,而2019年我采用了云开发技术独立开发的小程序后效果比之前的效果好了10倍。 接下来我就来说说这整个运营过程中的变化。 开始 2017年10月,开启帮你养成好习惯活动。 [图片] 活动 规则: 每天在文章下方留言评论,连续60天可获得奖励 奖励: 定制笔记本纪念套装 目的: 增加用户对公众号的粘性 活动刚开始运营的时候效果非常不错,近1个月都是每天基本上都是100条左右,效果超出了我的预期。 [图片] 但是好景不长,过了一个月之后80条左右,再过了一个月50条左右,再过一个月30条左右。。。。慢慢的参与的人就越来越少了。 [图片] 激活 到了2018年,我想再次激活活动加强奖励,于是在2018年的10月份又开启了新的一轮活动,虽然规则一样但是奖励多了。 [图片] 活动 在原来的基础上新增了100天的奖励,定制衣服一件。 本以为效果可以像之前一样,但是没想到效果大不如从前。 [图片] 并且效果比去年失效的更快,一个月的时间留言数量就到了20左右。 [图片] 本次激活策略以失败告终。 重生 在2019年的3月份,我采用云开发制作了一个打卡小程序替代之前的留言活动方式。 [图片] 进入小程序,一键打卡即可。 [图片] 之前的打卡规则:通过留言的形式进行打卡,累计留言达一定天数还送专属福利。 在这个过程中存在3个问题: 对于运营:累计留言天数无法很精确的统计,只能通过看每天的留言数据。 对于读者:参与门槛过高,在通勤的路上不方便打字留言,参与人数很少。 对于读者:很多时候忘记打卡,没有一个提醒机制,不能查看打卡记录。 运营成本过高,参与成本也过高。 做这款小程序的目的就是降低这两个成本,顺便学习下云开发相关技术。 以下是打卡流程图: [图片] 数据对比 参与打卡: 从3月份到9月份,共6个月的时间累计打卡次数57235次。 平均每个月打卡次数9539,平均每天打卡317人参与。 参与留言: 从2017年10月到2019年3月份,共17个月累计留言14749条。 平均每个月打卡次数867,平均每天留言29人参与。 注:按每个月30天来算。 结果 采用小程序后参与活动人数对比之前提高了10倍。
2020-11-23 - 小程序如何生成海报分享朋友圈
摘要: 小程序开发必备技能啊… 原文:小程序如何生成海报分享朋友圈 作者:小白 Fundebug经授权转载,版权归原作者所有。 项目需求写完有一段时间了,但是还是想回过来总结一下,一是对项目的回顾优化等,二是对坑的地方做个记录,避免以后遇到类似的问题。 需求 利用微信强大的社交能力通过小程序达到裂变的目的,拉取新用户。 生成的海报如下: [图片] 需求分析 1、利用小程序官方提供的api可以直接分享转发到微信群打开小程序 2、利用小程序生成海报保存图片到相册分享到朋友圈,用户长按识别二维码关注公众号或者打开小程序来达到裂变的目的 实现方案 一、分析如何实现 相信大家应该都会有类似的迷惑,就是如何按照产品设计的那样绘制成海报,其实当时我也是不知道如何下手,认真想了下得通过canvas绘制成图片,这样用户保存这个图片到相册,就可以分享到朋友圈了。但是要绘制的图片上面不仅有文字还有数字、图片、二维码等且都是活的,这个要怎么动态生成呢。认真想了下,需要一点一点的将文字和数字,背景图绘制到画布上去,这样通过api最终合成一个图片导出到手机相册中。 二、需要解决的问题 二维码的动态获取和绘制(包括如何生成小程序二维码、公众号二维码、打开网页二维码) 背景图如何绘制,获取图片信息 将绘制完成的图片保存到本地相册 处理用户是否取消授权保存到相册 三、实现步骤 这里我具体写下围绕上面所提出的问题,描述大概实现的过程 ①首先创建canvas画布,我把画布定位设成负的,是为了不让它显示在页面上,是因为我尝试把canvas通过判断条件动态的显示和隐藏,在绘制的时候会出现问题,所以采用了这种方法,这里还有一定要设置画布的大小。 [代码]<canvas canvas-id="myCanvas" style="width: 690px;height:1085px;position: fixed;top: -10000px;"></canvas> [代码] ②创建好画布之后,先绘制背景图,因为背景图我是放在本地,所以获取 <canvas> 组件 canvas-id 属性,通过createCanvasContext创建canvas的绘图上下文 CanvasContext 对象。使用drawImage绘制图像到画布,第一个参数是图片的本地地址,后面两个参数是图像相对画布左上角位置的x轴和y轴,最后两个参数是设置图像的宽高。 [代码]const ctx = wx.createCanvasContext('myCanvas') ctx.drawImage('/img/study/shareimg.png', 0, 0, 690, 1085) [代码] ③创建好背景图后,在背景图上绘制头像,文字和数字。通过getImageInfo获取头像的信息,这里需要注意下在获取的网络图片要先配置download域名才能生效,具体在小程序后台设置里配置。 获取头像地址,首先量取头像在画布中的大小,和x轴Y轴的坐标,这里的result[0]是我用promise封装返回的一个图片地址 [代码]let headImg = new Promise(function (resolve) { wx.getImageInfo({ src: `${app.globalData.baseUrl2}${that.data.currentChildren.headImg}`, success: function (res) { resolve(res.path) }, fail: function (err) { console.log(err) wx.showToast({ title: '网络错误请重试', icon: 'loading' }) } }) }) let avatarurl_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,所以我想用另一个方法来实现,就是先获取正常情况下一个字的宽度值,然后乘以总字数就获得了总宽度,亲试是可以的。 [图片] [代码]let allReading = 97 / 6 / app.globalData.ratio * wordNumber.toString().length + 325; ctx.font = 'normal normal 30px sans-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 = true wx.saveImageToPhotosAlbum({ filePath: canvasToTempFilePath, success() { that.setData({ showShareImg: false }) wx.showToast({ title: '图片保存成功,快去分享到朋友圈吧~', icon: 'none', duration: 2000 }) }, fail() { wx.showToast({ title: '保存失败', icon: 'none' }) } }) }, fail() { // 如果用户拒绝过或没有授权,则再次打开授权窗口 openStatus = false console.log('请设置允许访问相册') wx.showToast({ title: '请设置允许访问相册', icon: 'none' }) } }) } else { // 有则直接保存 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(err) { console.log(err) } }) } [代码] 总结 至此所有的步骤都已实现,在绘制的时候会遇到一些异步请求后台返回的数据,所以我用promise和async和await进行了封装,确保导出的图片信息是完整的。在绘制的过程确实遇到一些坑的地方。比如初开始导出的图片比例大小不对,还有用measureText测量文字宽度不对,多次绘制(可能受网络原因)有时导出的图片上的文字颜色会有误差等。如果你也遇到一些比较坑的地方可以一起探讨下做个记录,下面附下完整的代码 [代码]import regeneratorRuntime from '../../utils/runtime.js' // 引入模块 const app = getApp(), api = require('../../service/http.js'); var ctx = null, // 创建canvas对象 canvasToTempFilePath = null, // 保存最终生成的导出的图片地址 openStatus = true; // 声明一个全局变量判断是否授权保存到相册 // 获取微信公众号二维码 getCode: function () { return new Promise(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) }) }) }, // 生成海报 async createCanvasImage() { let that = this; // 点击生成海报数据埋点 that.setData({ generateId: '点击生成海报' }) if (!ctx) { let codeUrl = await that.getCode() wx.showLoading({ title: '绘制中...' }) let code = new Promise(function (resolve) { wx.getImageInfo({ src: codeUrl, success: function (res) { resolve(res.path) }, fail: function (err) { console.log(err) wx.showToast({ title: '网络错误请重试', icon: 'loading' }) } }) }) let headImg = new Promise(function (resolve) { wx.getImageInfo({ src: `${app.globalData.baseUrl2}${that.data.currentChildren.headImg}`, success: function (res) { resolve(res.path) }, fail: function (err) { console.log(err) wx.showToast({ title: '网络错误请重试', icon: 'loading' }) } }) }) Promise.all([headImg, code]).then(function (result) { const ctx = wx.createCanvasContext('myCanvas') console.log(ctx, app.globalData.ratio, 'ctx') let canvasWidthPx = 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 = 'normal bold 44px sans-serif'; ctx.setFillStyle('#ffffff'); // 文字颜色 ctx.fillText(wordNumber, 325, 153); // 绘制文字 ctx.font = 'normal normal 30px sans-serif'; ctx.setFillStyle('#ffffff') ctx.fillText('字', allReading, 150); ctx.font = 'normal normal 24px sans-serif'; ctx.setFillStyle('#ffffff'); // 文字颜色 ctx.fillText('打败了全国', 26, 190); // 绘制文字 ctx.font = 'normal normal 24px sans-serif'; ctx.setFillStyle('#faed15'); // 文字颜色 ctx.fillText(that.data.percent, 154, 190); // 绘制孩子百分比 ctx.font = 'normal normal 24px sans-serif'; ctx.setFillStyle('#ffffff'); // 文字颜色 ctx.fillText('的小朋友', 205, 190); // 绘制孩子百分比 ctx.font = 'normal bold 32px sans-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 = 'normal normal 28px sans-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 = 'normal normal 18px sans-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.tempFilePath that.setData({ showShareImg: true }) console.log(res.tempFilePath, 'canvasToTempFilePath') wx.showToast({ title: '绘制成功', }) }, fail: function () { wx.showToast({ title: '绘制失败', }) }, complete: function () { wx.hideLoading() wx.hideToast() } }) }) }) } }, // 保存到系统相册 saveShareImg: function () { let that = 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 = true wx.saveImageToPhotosAlbum({ filePath: canvasToTempFilePath, success() { that.setData({ showShareImg: false }) wx.showToast({ title: '图片保存成功,快去分享到朋友圈吧~', icon: 'none', duration: 2000 }) }, fail() { wx.showToast({ title: '保存失败', icon: 'none' }) } }) }, fail() { // 如果用户拒绝过或没有授权,则再次打开授权窗口 openStatus = false console.log('请设置允许访问相册') wx.showToast({ title: '请设置允许访问相册', icon: 'none' }) } }) } else { // 有则直接保存 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(err) { console.log(err) } }) } }, [代码]
2019-06-15