- H5如何跳转微信小程序?
之前遇到一个需求,就是要从H5跳转到小程序里,但是微信之前一直没有提供接口做跳转,我们只能做降级方案,在要跳转小程序的地方做了一个弹窗,弹窗里面放小程序码,引导用户长按识别小程序码,然后跳转到小程序内,整个流程非常之长,转化率可想而知也是很低的。 今天刚好看到有人技术群里面问了这个问题,于是我就去看了下微信的文档,发现微信偷偷的更新的这个接口,可以让微信浏览器下的H5跳转到小程序内。 相关文档在这边: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html 用的是JS-SDK的接口,需要使用到js-sdk-1.6.0的版本才有支持,https://res.wx.qq.com/open/js/jweixin-1.6.0.js wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印 appId: '', // 必填,公众号的唯一标识 timestamp: , // 必填,生成签名的时间戳 nonceStr: '', // 必填,生成签名的随机串 signature: '',// 必填,签名 jsApiList: [], // 必填,需要使用的JS接口列表 openTagList: [] // 可选,需要使用的开放标签列表,例如['wx-open-launch-app'] }); 在wx.config下面多了一项openTagList,开放标签列表,目前支持配置wx-open-launch-weapp,wx-open-launch-app wx-open-launch-weapp 指H5跳转小程序 wx-open-launch-app 指H5跳转app 我们主要介绍的是wx-open-launch-weapp H5跳转小程序 先上才艺: [图片][图片][图片] html代码如下: var btn = document.getElementById('launch-btn'); btn.addEventListener('launch', function (e) { console.log('success'); }); btn.addEventListener('error', function (e) { console.log('fail', e.detail); }); username为小程序的原始id,path对应的是小程序的链接地址。之前有写过微信H5的应该会知道怎么把这段代码嵌入到之前的代码里面。 目前此功能仅开放给已认证的服务号,网页的域名要在服务号的“JS接口安全域名”下。 亲测<wx-open-launch-weapp>可以跳转到任意合法合规的小程序,是任意的小程序都能跳转!!!!这个接口真开放(不怕人干坏事?) PS: 有个坑,官方文件说的path是/a/b/c?d=1&e=2#fg,类似的这样的链接格式,但是我自己亲测如果直接使用/a/b/c?d=1&e=2#fg这样格式的链接会报页面不存在,然后我想到了小程序那边复制链接的时候会在链接后面加上.html,于是挖槽的事情发生了,把path链接格式换成/a/b/c.html?d=1&e=2#fg这样就能正常访问,不知道是微信故意这样设计的还是bug,有待考证。 然后这个接口真的可以干好多坏事,希望大家能用正确的价值观来正确使用此接口。 微信开放标签有最低的微信版本要求,以及最低的系统版本要求。 如果开发过程中出现以下情况的,要确认一下,微信版本要求为:7.0.12及以上。 系统版本要求为:iOS 10.3及以上、Android 5.0及以上。 [图片]
2020-07-09 - wx.openDocument打开文件后,标题位置能显示文件名吗?
实现文档查看的功能, 调用wx.openDocument 可以实现,但是此预览页标题为空白, 请问是否支持将文件名作为文件预览页的标题? 刚才试了一下,在微信中直接打开文档,也没有文件名作为标题,不过至少有“文件预览”几个字。
2019-02-19 - 微信开发工具直接编译scss文件
前言 以前微信开发工具不支持打卡scss文件,每次一个小改动,都要切换其他编辑器编译,感觉比较麻烦.最近 RC Build (1.02.2001191) 的开发工具支持打开scss文件,这算是一个福音,然而如何编译成wxss却没有说明,所以自己利用gulp在开发工具中直接编译,很方便. 使用 1、在项目与app.js同级目录中新建文件gulpfile.js 加入内容如下: var gulp = require('gulp'); var sass = require('gulp-sass'); var rename = require('gulp-rename') var changed = require('gulp-changed') var watcher = require('gulp-watch') //自动监听 gulp.task('default', gulp.series(function() { watcher('./pages/**/*.scss', function(){ miniSass(); }); })); //手动编译 gulp.task('sass', function(){ miniSass(); }); function miniSass(){ return gulp.src('./pages/**/*.scss')//需要编译的文件 .pipe(sass({ outputStyle: 'expanded'//展开输出方式 expanded })) .pipe(rename((path)=> { path.extname = '.wxss' })) .pipe(changed('./pages'))//只编译改动的文件 .pipe(gulp.dest('./pages'))//编译 .pipe(rename((path)=> { console.log('编译完成文件:' + 'pages\\' + path.dirname + '\\' + path.basename + '.scss') })) } 复制代码 2、打开命令行,进入gulpfile.js所在目录,执行如下命令 [代码]npm install[代码][代码]gulp-sass[代码][代码]gulp-rename[代码][代码]gulp-changed[代码]3、执行监听命令 [代码]gulp[代码] 会监听pages目录所有的scss文件变动,保存后会自动编译.
2020-02-24 - 还在傻傻分不清ES5、Es6数组方法?各大姿势来袭
前言 初衷: 在面试中,面试官经常问到说一下Es5和Es6的数组方法有哪些,有很多同学老是分不清楚,今天笔者就来分享一下。 适合人群: 前端初级开发 感兴趣的小伙伴点击链接,了解详情~ http://github.crmeb.net/u/yi Es5系列 indexOf 用途: 用于查找数组中是否存在某个值,如果存在则返回某个值的下标,否则返回[代码]-1[代码] let list = [1, 2, 3]; console.log(list.indexOf(2)) // 1 console.log(list.indexOf("蛙人")) // -1 map 用途: [代码]map[代码]是一个数组函数方法,接收三个参数,[代码]value[代码],[代码]index[代码],[代码]self[代码],返回值是处理完的结果。 let list = [1, 2, 3]; const res = list.map((value, key, self) => { console.log(value) // 1 2 3 console.log(key) // 0 1 2 console.log(self) // [1, 2, 3] return value * 2 }) console.log(res) forEach 用途: 用于遍历一个数组,接收三个参数,[代码]value[代码],[代码]index[代码],[代码]self[代码],返回值为[代码]undefined[代码] let list = [1, 2, 3]; const res = list.forEach((value, key, self) => { console.log(value) // 1 2 3 console.log(key) // 0 1 2 console.log(self) // [1, 2, 3] return 123 }) console.log(res) // undefined splice 用途: 用于数组删除或替换内容,接收三个参数: 第一个参数是,删除或添加的位置第二个参数是,要删除的几位,如果为0则不删除第三个参数是,向数组添加内容 let list = [1, 2, 3]; list.splice(0, 1) // 把第0个位置,给删除一位 console.log(list) // [2, 3] list.splice(0, 1, "蛙人") // 把第0个位置,给删除一位,添加上一个字符串 console.log(list) // ["蛙人", 2, 3] list.splice(0, 2, "蛙人") // 把第0个位置,给删除2位,添加上一个字符串 console.log(list) // ["蛙人", 3] slice 用途: 用于截取数组值,接收两个参数,第一个参数是要获取哪个值的下标,第二个参数是截取到哪个下标的前一位。 let list = [1, 2, 3]; let res = list.slice(1, 3) // 从第一位下标开始截取,到第三位下标的前一位,所以截取出来就是 [2, 3] console.log(res) // [2, 3] filter 用途: 用于过滤数组内的符合条件的值,返回值为满足条件的数组对象 let list = [1, 2, 3]; let res = list.filter(item => item > 1); console.log(res) // [2, 3] every 用途: 用于检测数组所有元素是否都符合指定条件,返回值为[代码]Boolean[代码] , 该方法是数组中必须全部值元素满足条件返回[代码]true[代码],否则[代码]false[代码] let list = [1, 2, 3]; let res = list.every(item => item > 0) console.log(res) // true let res1 = list.every(item => item > 1) console.log(res1) // false some 用途: 用于检测数组中的元素是否满足指定条件,返回值为[代码]Boolean[代码] , 该方法是只要数组中有一项满足条件就返回[代码]true[代码],否则[代码]false[代码] let list = [1, 2, 3]; let res = list.some(item => item > 0) console.log(res) // true reduce 用途: 该方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。该方法回调函数接收四个参数 第一个参数:初始值, 或者计算结束后的返回值第二个参数:当前元素第二个参数:当前元素的索引第四个参数:当前元素所属的数组对象,本身 我们一般只用前两个就行,[代码]reduce[代码]第一个参数回调函数,第二个参数是初始值 let list = [1, 2, 3]; let res = list.reduce(( prev, cur ) => prev += cur, 0) console.log(res) // 6 reverse 用途: 用于数组反转 let list = [1, 2, 3]; let res = list.reverse(); console.log(res) // [3, 2, 1] join 用途: 用于数据以什么形式拼接 let list = [1, 2, 3]; let res = list.join("-"); console.log(res) // 1-2-3 let sum = eval(list.join("+")) console.log(sum) // 6 sort 用途: 用于将数组排序,排序规则看返回值 返回值为正数,后面的数在前面返回值为负数,前面的数不变,还在前面返回值为0,都不动 let list = [1, 2, 3]; let sort = list.sort((a, b) => b - a) console.log(sort) // [3, 2, 1] concat 用途: 用于合并数组原始 let list = [1, 2, 3]; let res = list.concat([1, 2, 3]) console.log(res) // [1, 2, 3, 1, 2, 3] push 用途: 向数组后面添加元素,返回值为数组的[代码]length[代码] let list = [1, 2, 3]; let res = list.push(1) console.log(res) // 4 pop 用途: 用于删除数组尾部的元素,返回值为删除的元素 let list = [1, 2, 3]; let res = list.pop() console.log(res) // 3 shift 用途: 用于删除数组的头部,返回值为删除的元素 let list = [1, 2, 3]; let res = list.shift() console.log(res) // 1 unshift 用途: 向数组的头部添加元素,返回值为数组的[代码]length[代码] let list = [1, 2, 3]; let res = list.unshift(1) console.log(res) // 4 toString 用途: 用于将数组内容转换为字符串 let list = [1, 2, 3]; let res = list.toString() console.log(res) // 1,2,3 Es6+ 系列 includes 用途: 检测数组中是否存在该元素,返回[代码]Boolean[代码]值 let list = [1, 2, 3]; let res = list.includes("蛙人") let res1 = list.includes(1) console.log(res, res1) // false true find 用途: 查找数组的元素,满足条件的返回单个值,按照就近原则返回 let list = [1, 2, 3]; let res = list.find((item) => item > 1) console.log(res) // 2, 按照就近原则返回 findIndex 用途: 查找数组中元素,满足条件的返回数组下标 let list = [1, 2, 3]; let res = list.findIndex((item) => item > 1) console.log(res) // 1, 按照就近原则返回下标 flat 用途: 用于拉平嵌套数组对象 let list = [1, 2, 3, [4, [5]]]; let res = list.flat(Infinity) console.log(res) // [1, 2, 3, 4, 5] fill 用途: 用于填充数组对象 let list = [1, 2, 3]; let res = list.fill(1) console.log(res) // [1, 1, 1] Array.isArray 用途: 检测对象是不是一个数组 let list = [1, 2, 3]; let res = Array.isArray(list) console.log(res) // true Array.from 用途: 将伪数组转换为真数组 let res = Array.from(document.getElementsByTagName("div")) console.log(res) // 转换为真数组就可以调用数组原型的方法 Array.of 用途: 用于生成一个数组对象,主要是用来弥补[代码]Array()[代码]的不足 let res = Array.of(1, 2, 3) console.log(res) // [1, 2, 3] 改变原始数组值的有哪些[代码]splice[代码]、[代码]reverse[代码]、[代码]sort[代码]、[代码]push[代码]、[代码]pop[代码]、[代码]shift[代码]、[代码]unshift[代码]、[代码]fill[代码] 结语这里[代码]keys[代码]、[代码]values[代码]、[代码]entries[代码]就不写啦,它们使用数组方式的话,返回的是[代码]Iterator[代码]遍历器对象。欢迎大家查漏补缺常用数组方法哦 感谢谢谢你读完本篇文章,希望对你能有所帮助,如有问题欢迎各位指正。 感兴趣的小伙伴点击链接,了解详情~ http://github.crmeb.net/u/yi 作者:蛙人
2021-04-02 - 盘点15种小程序营销玩法,实现高引流、高裂变、高转化、高复购!
在电商小程序的运营中,得有店提供了丰富多样的工具辅助商家做好小程序运营,在这里,得有店也分享了一些常见的有效的运营玩法,帮助商家实现高引流、高裂变、高转化、高复购。 [图片] 一、新客有礼 功能说明:“发券宝”是一款定向发券工具,可以针对新用户进入商城后发放新客大礼包,可以是针对指定商品的优惠券,或无门槛优惠券,促进新人的首笔订单转化。 推荐理由:发放内容是针对指定人群的推送(如新人、会员、客户特定身份等),做到精准营销,不“骚扰”客户,提升了打开率。 玩法示例:小程序刚上线,没有用户基础,可以引导现有客户或到店客户进入小程序下单,可以领取一张无门槛的优惠券,以后也会定期推送优惠信息,达到快速拉新的目的。 [图片] 二、拼团 功能说明:拼团是社交电商常用的玩法,拉新效果可谓一流。以超低的拼团价吸引用户分享邀请好友参团,达到指定成团人数即可拼团成功,拼团与小程序的结合能把拼团拉新的价值进一步放大。 推荐理由:拼团活动能够为小程序带来更大曝光的同时,也可以给新品试水,测试市场反馈,也可以去库存,提高销量。 玩法示例:如果想快速打开小程序的知名度,可以选择知名度高的热销品作为拼团商品,在保证不亏本的前提下,可以大大降低利润,以低价刺激用户参团分享传播。 [图片] 三、砍价 功能说明:通过分享、邀请好友帮忙砍价,即可享受超低价购买商品的福利(可以是0元购)。通过利益刺激用户分享传播,而对于被分享的好友而言,这是一种低成本的举手之劳。 推荐理由:通过参与者的分享,实现快速裂变传播,在增加粉丝的同时,提升店铺销量与品牌知名度。 玩法示例:餐饮店以“1元吃自助牛排”的砍价活动,可设置40人帮砍,以此实现快速吸粉。 [图片] 四、秒杀 功能说明:通过设定高优惠的限时折扣活动吸引粉丝进行快速转化,实现新品推广,清库存等目的。 推荐理由:限时限量抢购,营造促销的紧张感,快速拉升人气,有效刺激消费,让商家销量快速上涨。 玩法示例:设置秒杀专题,5个商品参与秒杀,给客户选择性,其中一款是核心爆款,吸引用户的参与度。如原价199元的商品,秒杀价只需39.9元。 [图片] 五、门店引流 功能说明:通过一定的手段,如开启“附近的小程序”,以优惠信息的展示吸引用户到店;也可以周边发券,到店核销使用;在网上0.01元秒杀或拼团的商品仅限到店自提”,将线上流量引流到门店,让门店与用户产生更直接、更紧密的连接,拉动实体门店的业绩增长。 推荐理由:零售企业通过小程序打通了线上线下,借助小程序天然流量优势为实体门店带去客流,赋能实体门店,带动门店业绩提升。 玩法示例:水果店发起1分钱拼团活动,10人成团购买一盒樱桃,仅限到店自提,线上裂变带来新客户,也为线下门店带了客流,同时带动门店其他水果的销售。 [图片] 六、满减 功能说明:支持满N元减、满N件减、满N元打折、满N件打折等多种营销场景,能够刺激用户为了享更多优惠而选择凑单购买,从而提升了客单价,带动了销量。 推荐理由:重大节日最常用的营销玩法,如618,双11,能有效刺激客户为了享受优惠而凑单购买或许当下并不需要的东西,商家实现快速营收,也达到了清库存的目的。 玩法示例:对于过季没卖完的衣服需要清库存,可以设置阶梯优惠,“买2件打8折、买3件打7折、买4件打6折”,用户到店后为了获得更多的优惠,进行凑单购买。 [图片] 七、分销员 功能说明:分销是一款店铺利用客户推广带来流量与销量的营销工具。商家可通过招募分销员吸引用户注册,利用佣金的奖励方式刺激粉丝进行推广,提高店铺转化率。 推荐理由:在快速裂变获客的同时,佣金模式还可激励用户传播动力的持久性。 玩法示例:一家母婴用品店有自己的宝妈粉丝群,为了吸引更多精准粉丝,促进销售,制定了分销佣金计划,如月成交5单,每单可拿5%佣金;月成交20单,每单可拿10%的佣金;从而拉动200个分销员,业绩最好的分销员一月可成交一万元。 [图片] 八、签到有礼 功能说明:签到功能的核心规则在于连续签到时间越长,可获取的收益越大,中途中断签到,则连续天数归零重新计算,从而实现用户与商家的持续互动。 推荐理由:签到享好礼,在提升客户活跃度的同时,提高了消费复购率。 九、会员卡 功能说明:会员卡功能,包括会员卡创建体系、开卡方式、动态会员成长值和会员权益等,旨在帮助商家寻找优质用户,固化会员消费群体,提高店铺复购率。 推荐理由:会员消费升级机制,鼓励粉丝通过不断消费享受会员特权,提升了复购率。 玩法示例:可设置不同的会员卡样式,不同的会员可享受对应的会员权益,线上线下可结合使用,方便商家第一时间掌握会员信息,以便日后进行回访、优惠活动精准宣传等操作。 [图片] 十、积分商城 功能说明:积分商城是一款会员使用账户积分兑换商品的营销工具,店铺通过设置具有吸引力的积分商品提高用户活跃度与忠诚度。 推荐理由:提高小程序日活,促进用户二次消费,提高特权感,增加会员粉丝粘性,提升会员复购率。 玩法示例:可将刚需的日常生活用品以积分的形式进行购买,也可针对热销爆品,以低价+积分的形式进行换购,用户会为了积累足够的积分而持续访问店铺,产生消费。 [图片] 十一、会员储值 功能说明:一款可帮助商家提升客户忠诚度、增强会员粘性的插件应用。商家可根据需要新建储值规则,并配置不同的储值营销活动来招募更多的新会员和刺激会员消费。 推荐理由:商家可以提前预收客户的消费金额,回收资金,同时培养客户的忠诚度,刺激反复消费。 玩法示例:生鲜水果店、蛋糕店比较常用的玩法,充多少可享折扣,锁定客户,刺激持续复购。 十二、直播 功能说明:小程序直播更适合随时随地观看购买的场景。商户可以通过手机跟粉丝直播互动,让商户更亲民的进行品牌宣传、产品售卖、客户关系维护,为商户打开视频营销的流量入口。 推荐理由:用户可在小程序上预约直播,在直播过程中商家可推送商品,用户可点击购物袋直接查看并购买,实现实时互动、边看边买。 同时,用户还可以对直播进行评论,主播也可根据评论实时互动,如在直播间发放礼盒等,从而增加用户与商户之间的亲近感。 玩法示例:直播带货的魅力,很多人应该都深有体会,某某十分钟带货几千万。商家可将一些热销的爆品、新品预售放在直播间去买,用户边看边买,还可抽奖、领礼品,能快速吸引一大波粉丝。 [图片] 十三、在线预约 功能说明:在线报名登记,在线预约服务等均可设置预约表单,吸引用户到店体验或消费,可提高消费者体验,也带动了店铺营收。 推荐理由:商家或门店发布预约,邀请客户到店,将线上流量引流到门店,助力商家提高运营效率,提升用户体验,促进用户消费。 玩法示例:服装新款免费试穿,提交试穿预约表单,到店消费可享大力折扣;培训班招募学员,在线报名,可享一周免费课体验。 十四、专题文章 功能说明:商户可以通过创建优质内容、管理专题文章,刺激用户购买欲望,提升转化率,提高店铺流量与销量。 推荐理由:店铺可多种商品搭配销售,在提升关联商品销量的同时,让消费者的个性需求获得满足。 玩法示例:如主营上班族日常穿搭的商户,在使用专题文章后,新品服饰通过专题文章进行发布,展示各场景需要的职业套装,附带介绍同款的休闲搭配;不仅带来粉丝的关注,也增加了商户促销的活动形式。 [图片] 十五、小程序+广告 功能说明:无论是小程序广告、公众号文中广告、公众号底部广告或是朋友圈广告,都能够帮助商家通过社交传播触达微信活跃用户,快速获取潜在客户。 推荐理由:小程序作为广告落地页轻量易用、可反复触达客户,有更好的用户体验和更佳的转化效率,助力企业在展示创意的同时打通营销链路,品效合一。 玩法示例:如休闲零售店可自定义用户标签、区域、年龄等用户画像,通过小程序+朋友圈广告出现在目标客户的朋友圈,这样打通了从浏览到购买的营销链路,在节日期间用爆款带动传播,能有效提升品牌的影响力,增加商城销量。 [图片] 不同的行业特点,不同的商家规模,所采取的主要小程序运营手段会有所不同,一般情况下,都是两种以上的组合玩法运营小程序,才有可能达到预期效果。
2020-10-27 - 小程序AR识别,三行代码实现Camera数据毫秒级转base64图片
关键词:小程序AR 图片 base64 相机 Camera onCameraFrame Canvas ArrayBuffer Uint8Array Uint8ClampedArray upng-js 核心步骤: 1 相机原始图像数据frame.data,即ArrayBuffer数组,转成Uint8Array数组 2 Uint8Array数组转成Uint8ClampedArray数组 3 wx.canvasPutImageData(Uint8ClampedArray) 详细流程如下: 最近因为项目需求,需要上传base64去做AR识别功能,和大家一起分享讨论下具体的实现方式。 首先说下实现原理,通过Camera的onCameraFrame获取实时帧数据,将实时帧数据添加到Canvas上,然后将Canvas保存为临时图片,再将临时图片转换为base64。 贴上核心实现代码: wxml: js: var nCounter = 0; openCamera: function (res) { var that = this var camera_ctx = wx.createCameraContext() listener = camera_ctx.onCameraFrame((frame) => { // nCounter等于30 是因为一开始相机会有一个对焦的过程,如果一开始获取数据,就是模糊的图片 if (nCounter == 30) { console.log(frame.data instanceof ArrayBuffer, frame.width, frame.height) var data = new Uint8Array(frame.data); var clamped = new Uint8ClampedArray(data); // 实时帧数据添加到Canvas上 wx.canvasPutImageData({ canvasId: 'myCanvas', x: 0, y: 0, width: frame.width, height: frame.height, data: clamped, success(res) { // 转换临时文件 wx.canvasToTempFilePath({ x: 0, y: 0, width: frame.width, height: frame.height, canvasId: 'myCanvas', fileType: 'jpg', destWidth: frame.width, destHeight: frame.height, // 精度修改 quality: 0.8, success(res) { // 临时文件转base64 wx.getFileSystemManager().readFile({ filePath: res.tempFilePath, //选择图片返回的相对路径 encoding: 'base64', //编码格式 success: res => { // 保存base64 that.data.mybase64 = res.data; } }) }, fail(res) { console.log(res); } }, that) } }) } nCounter++ // console.log(nCounter); if (nCounter >= 100) { nCounter = 0 } }) listener.start() } 目前网上有两种转换方式并对比下: 1:upng-js等第三方转码js库,将相机流转换成base64,一般需要1-2s左右 [图片] 2.使用canvas将相机流转变base64,都是使用js或者小程序官方的api进行转换,一般转换时间在1秒以下: [图片] 重点说明下: 如何使用wx.canvasPutImageData()将相机流添加canvas,我们查看该官方api,添加的data类型为:Uint8ClampedArray [图片] 而我们通过onCameraFrame获取的data类型为:ArrayBuffer [图片] 所有两者类型不一致,就需要转换,将ArrayBuffer=>Uint8Array=>Uint8ClampedArray var data = new Uint8Array(frame.data); var clamped = new Uint8ClampedArray(data); 成功的把onCameraFrame获取实时帧数据转换并canvasPutImageData在canvas上,并通过canvasToTempFilePath获取临时文件,如何获取临时文件getFileSystemManager转换为base64,传入云端进行AR识别,就大功告成! 技术分享来自于:北京晞翼科技有限公司 技术作者:le3d618、xiaoz0816 微信商务联系:le3d618
2020-04-30 - 持续更新:收藏整理官方隐藏的小程序功能/参数/方法/API
简介 一门热门的编程语言通常会留下一些带惊喜的「彩蛋」让你去自己深挖,小程序也是如此。此文是收集与整理官方未公布的一些彩蛋。希望大家多多提供线索。本文仅整理官方未公布的小程序相关包括但不限于:组件属性、功能、方法、API。 组件类 scroll-view里面的隐藏属性 throttle="{{false}}" 功能:关闭函数节流功能,可以更精准的bindScroll。但也更耗手机性能 [代码]app.json里面配置关闭同层渲染功能: "window": { "renderingMode": "seperated" } [代码] 收集中… 功能类 wx:for支持Object列表渲染 [代码]{ '2018-1-9':{ address: '....', name: '....' }, '2018-1-10':{ address: '....', name: '....' }, '2018-1-11':{ address: '....', name: '....' } } //wxml <view wx:for="{{obj}}" wx:for-index="key" wx:for-item="value"> {{key}} : {{value.address}} </view> [代码] 2.wx.chooseContact 作用:从手机通讯录中选择联系人并获取相关信息(iPhone下有兼容问题) [代码]参考代码: wx.chooseContact({ success:res=>{ that.customer.name = res.displayName; that.customer.contract = res.phoneNumber; }, complete: function(res) { } }); [代码] 收集中… 开发者工具类 完美支持各种插件安装,详见: 动手打造更强更好用的微信开发者工具-编辑器扩展篇 | 微信开放社区 https://developers.weixin.qq.com/community/develop/article/doc/000a8816e18748dd52f96f8975b413 历史版本的隐藏下载链接 升级开发者工具到最新,种种原因需要恢复旧版,旧版安装包找不到应急处理方法 | 微信开放社区 https://developers.weixin.qq.com/community/develop/article/doc/0008c4833f4450f8e32ab0dbc51013 官方社区类 有几个隐藏的官方的美女甩锅采用非官方账号在社区带节奏。具体我就不点名了。🐶🐶🐶🐶 收集中… 其他未公布的隐藏功能: 给此文点赞后的码农写代码永无BUG ,CP设计的产品人见人爱,BOSS每年收入翻番! ↓↓↓↓
2020-06-26 - 【新人报道】尝鲜小程序自动化测试工具(miniprogram-automator)
测试驱动简介 关于测试驱动的快速入门,可以看之前的这篇文章《 换一种思路写代码,前端测试驱动开发模式(TDD)快速入门》,这篇文章就跳过一些基础的概念。 小程序自动化 来自官方的介绍: [代码]小程序自动化 SDK 为开发者提供了一套通过外部脚本操控小程序的方案,从而实现小程序自动化测试的目的。[代码] 个人理解,这个小程序自动化工具,就是可以用代码去写一些脚本,可以操作[代码]小程序开发者工具[代码]自动的执行一些操作。 官方文档: https://developers.weixin.qq.com/miniprogram/dev/devtools/auto/ 工具的安装和配置 运行环境(来自官方文档): 安装 Node.js 并且版本大于 8.0 基础库版本为 2.7.3 及以上 开发者工具版本为 1.02.1907232 及以上 首先 npm init, 初始化一个项目。 执行下面命令安装工具: [代码]npm i miniprogram-automator --save-dev [代码] 因为需要使用 jest 做为测试的工具,所以需要安装 jest,执行以下两条命令: [代码]npm i jest --save-dev npm i jest -g [代码] 在目录下创建 test 文件夹,创建以[代码].spec.js[代码]结尾的文件,在此文件中写入测试代码, 我这里建立[代码]index.spec.js[代码]。在 package.json 的 script 中添加: [代码]{ "test": "jest" } [代码] 这样[代码]npm run test 文件名[代码]就可以运行指定的测试文件。 连接小程序开发工具 我在尝鲜的时候在这里花了一些时间在连接小程序开发工具上。 首先需要在微信开发者工具, 设置->通用设置->安全中把服务端口打开。 [图片] 在[代码]index.spec.js[代码]中添加连接的代码: [代码]import automator from 'miniprogram-automator' describe('index', () => { let miniProgram // beforeAll 表示在执行所有测试之前需要的操作 beforeAll(async () => { miniProgram = await automator.launch({ cliPath: `${cli.bat文件的路径}`, // 如果是默认安装路径,就不需要写了 projectPath: `${项目路径}` //这里需要写上项目路径 }) }, 40000) }) [代码] 这时候就可以执行命令来测试连接小程序开发者工具了, 如果连接成功的话,在执行命令后,就会自动打开微信开发者工具,如果连接失败,就是命令行工具会关闭,并且打不开微信开发者工具。 一些连接经验: 为了路径兼容 windows 和 mac 最好用 node 的 path 模块去拼路径 cli.bat 是在微信开发者工具的根目录中,记得写路劲的时候要加上.bat 的后缀名 如果你是默认路径安装的,如果还是打不开,我自己就是遇到这个情况,我是通过 imweb 团队这篇文章中写的,打开自动化操作服务端口后就可以连接使用了 基本使用思路 目前这个工具只有四个 api: Automator, MiniProgram, Page, Element, 点击查看文档。 Automator 主要提供启动和连接开发者工具的方法。 MiniProgram 主要提供操作小程序的一些方法, 比如跳转页面,getSystemInfo, 或者执行 wx 对象上的方法 Page 主要提供操作页面的方法,比如用$获取元素,获取页面的 data,调用页面的方法 element 主要提供元素的操作方法 主要的使用思路就是用代码来操作小程序的操作。比如可以先用 MiniProgram 的页面跳转方法跳转到指定的页面,用 Page 的 callMethod 来执行相关的方法,再选取需要测试的元素,比较是否符合预期。 示例 这里展示一个小的示例。在电商小程序中,大部分页面都需要一进入就需要请求接口拿数据,我们通常把原始数据经过洗数据层变成前端想要的样子。现在就来测试一下是否初始的数据是我们期望的那样。 步骤是: 连接小程序,并且跳转的指定页面 等待初始接口调用 查看 page 的 data,比较是否符合预期 [代码]describe('index', () => { let miniProgram let page // 连接微信开发者工具 beforeAll(async () => { miniProgram = await automator.launch({ projectPath: `${root}` }) page = await miniProgram.reLaunch('/pages/plp/plp') // 使用miniProgram的api跳转到指定页面 await page.waitFor(500) // 等待页面请求接口 }, 40000) it('初始化洗数据后, 初始数据满足要求', async (done)=>{ const data = await page.data() // 使用page的方法获取当前页面的data expect(data).toMatchObject({ items: expect.anything(), facet: expect.anything() }) // 通过jest的expect方法比较这个data是否符合预期 done() }) 在执行 afterAll(async () => { await miniProgram.close() }) }) [代码] 执行 [代码]npm run test index[代码] 后,看到 pass 的字样,就表示这个测试通过了。 Jest 会自动把通过与不通过的测试用例都展示出来。 总结 小程序自动化测试工具 api 很简单,思路也简单易懂,可能连接的时候需要花一点时间。 实施好自动化测试可以提升程序本身的健壮性。 之后还要花一些时间来深入的使用。
2019-10-12 - request failed:ssl hand shake error
在开发过程中,iOS扫描预览一切都OK,可是安卓扫描就是不行,报错:ssl hand shake error 看到很多说是不支持TLS1.2的,我去网站上验证过,ATS是通过的,我的服务也是支持TLS1.2的。 [图片] 可是安卓还依旧报错:ssl hand shake error 求官方大大给个说法,到底是哪里的问题;看到好多一样的问题,每次出现的解决方案都是不一样;说明这不是一个定性问题吧;
2018-05-17 - 小程序利用safe-area-inset-*兼容iPhoneX
分别创建屏幕上边框,右边框,下边框,左边框安全距离: safe-area-inset-top, safe-area-inset-right, safe-area-inset-bottom, safe-area-inset-left 使用: iOS 11 padding-top: constant(safe-area-inset-top); padding-right: constant(safe-area-inset-right); padding-bottom: constant(safe-area-inset-bottom); padding-left: constant(safe-area-inset-left); iOS 11.2 beta及其后 padding-top: env(safe-area-inset-top); padding-right: env(safe-area-inset-right); padding-bottom: env(safe-area-inset-bottom); padding-left: env(safe-area-inset-left); 兼容性写法: padding-top: 10px; padding-top: constant(safe-area-inset-top); padding-top: env(safe-area-inset-top); 与calc合用: padding-top: 10px; padding-top: calc(10px + constant(safe-area-inset-top)); padding-top: calc(10px + env(safe-area-inset-top)); 终!使用sass@mixin: @mixin x-padding-bottom($val:0px) { padding-bottom: $val; padding-bottom: calc(#{$val / 2} + constant(safe-area-inset-bottom)); /* no */ padding-bottom: calc(#{$val / 2} + env(safe-area-inset-bottom)); /* no */ } 注意!!! 1、默认值为0px,不是0,原因是calc不支持与0计算。 2、小程序单位为rpx,一般都会转换为rpx,但是calc不支持,所以不允许转换,保持px。 参考文档:苹果官方文档
2019-10-11 - [填坑手册]小程序web-view组件实战与踩坑
[图片] 首先,根据官网文档可以知道 只有非个人 的小程序才可以使用web-view组件,如果你的个人开发者,可以跳过这篇文章。 [图片] 一、使用web-view以及它的好处 1、己方账号(第三方)与小程序openId/UnionId的关联绑定,实现免登陆 比如你是某门户网站S,你要识别自己小程序上的用户与网站用户的关系,你可以通过三种方法绑定关系,公众号,小程序源生,小程序web-view内嵌跳转三种方法 2、内嵌H5的富文本,减少重复开发 比如你是门户网站,社区,以往有大量的新闻和帖子,里面带了各种css样式的富文本,小程序源生是无法直接读取的,需要大量转化,这时候直接内嵌这些H5新闻,大大降低开发成本 3、热更新,减少发布审核 某些需要经常更新的内容、公告、活动页,内嵌H5可以减少频繁提交小程序审核 二、小程序功能赋权 为H5提供各种小程序才有的功能,比如录音,扫一扫等。 注意事项 多场景判断,建议使用官方API: wx.miniProgram.getEnv H5唤醒一些小程序API有一定的延时,0.3~1秒 请调用小程序专用的JSSDK,同一个jssdk,但是webview的功能收到限制,和之前微信打开H5有所不同 小程序自动获取加载H5的title H5中iframe的url必须也是业务域名 web-view一定是撑满全屏的,自定义顶部菜单,悬浮的都没用 三、小程序和H5之前的互相通讯 1、 从小程序 ==>> h5 小程序控制H5,可以直接用src路径传参的形式,比如 [代码]<!-- 小程序端 HTML --> <web-view src="//URL?a=param1&b=param2"></web-view> [代码] 避免在链接中带有中文字符,在 iOS 中会有打开白屏的问题,建议加一下 encodeURIComponent。 2、 从 H5 ==>> 小程序 [图片] 这里我们知道bindmessage是小程序用来监听H5的推送的内容,但是这里不大不小的坑!就是它的三个出发场景: 小程序后退:使用接口名 wx.miniProgram.navigateTo,wx.miniProgram.navigateBack,wx.miniProgram.switchTab 等切换小程序页面/场景的API时候都会出发 分享:这个就是当你点分享小程序的时候,会接受到H5之前发送的postMessage 组件销毁,web-view组件销毁,类似 wx.miniProgram.redirectTo 都会触发。 [代码]<!-- 小程序端 HTML --> <web-view bindmessage="handleGetMessage" src="{{openUrl}}"></web-view> [代码] [代码]// 小程序端 JS --> Page({ /** * 页面的初始数据 */ data: { openUrl: "", }, /** * 获取请求数据 */ handleGetMessage: function (e) { console.log(e.detail.data); } }, }) [代码] [代码]<!-- h5端 HTML和JS --> <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script> <script> wx.miniProgram.postMessage({ data: { link: "//test.com", title: "一起学习,一起进步" } }); //wx.miniProgram.redirectTo({ // url:"/pages/inner/index?source=123" //}) wx.miniProgram.navigateBack({delta: 1}) </script> [代码] 注意事项 那些H5控制小程序的跳转路径必须是“/”开头,如 “/pages/xxx/xxx”,且路径必须在app.json里有,地址错误的话,有时不报错。 postMessage的json必须是data开始,不然接收不到数据。 [图片] 四、bindmessage接收到消息有3个重要特性(重点) 接收可以是H5之前几分钟前发送postMessage,不一定是即刻发出的。 之前发出的 postMessage的DATA信息会累加,当触发bindmessage接收的时候是一个数组。 [图片] 当bindmessage 再次 接收到数据,之前发送的数据不会被清空,将累加一起返回,获取时要判断好数组的角标 [图片] 五、Tips 1、在IDE工具中如何调试H5 [图片] 可以在 web-view 组件上通过右键 - 调试,打开 web-view 组件的调试。 2、内嵌H5缓存问题 web-view加载的H5具有很重的缓存,如果需要调试,可以通过在url后面加时间戳的方式解决。 3、小程序关闭,H5背景音乐仍然在播放问题 小程序已经关闭,但是H5自带的背景音乐仍然在手机后台播放的问题。这里可以利用一个属性: visibilitychange:页面可见性状态 简单的说,浏览器标签页被隐藏或显示的时候会触发visibilitychange事件。 [代码]var statusBeforeHide = true; //初始化页面的状态 var music = document.getElementById("xxx"); // 更改音乐播放状态 function setChangeMusic() { if (document[hiddenProperty]) { // 页面隐藏 if (statusBeforeHide) { music.pause(); // 暂停 } } else { // 页面显示 if (statusBeforeHide) { music.play() //如果statusBeforeHide是true, 继续播放 } } } let hiddenProperty = ('hidden' in document) ? 'hidden' : ('webkitHidden' in document) ? 'webkitHidden' : ('mozHidden' in document) ? 'mozHidden' : null; if (hiddenProperty) { let visibilityChangeEvent = hiddenProperty.replace(/hidden/i, 'visibilitychange'); let onVisibilityChange = () => { //console.log('visibilityChange'); setChangeMusic(); }; document.addEventListener(visibilityChangeEvent, onVisibilityChange); } else { console.log("不支持这个api"); } [代码] 总结,web-view还是非常实用的组件,且用且珍惜~ 往期回顾: 小程序自定义头部导航栏“完美”解决方案 小程序Canvas生成海报(一) 小程序新版订阅消息+云开发实战与跳坑
2021-09-13 - [填坑手册]小程序Canvas生成海报(一)--完整流程
[图片] 海报生成示例 最近智酷君在做[小程序]canvas生成海报的项目中遇到一些棘手的问题,在网上查阅了各种资料,也踩扁了各种坑,智酷君希望把这些“填坑”经验整理一下分享出来,避免后来的兄弟重复“掉坑”。 [图片] 原型图 这是一个大致的原型图,下面来看下如何制作这个海报,以及整体的思路。 [图片] 海报生成流程 [代码片段]Canvas生成海报实战demo demo的微信路径:https://developers.weixin.qq.com/s/Q74OU3m57c9x demo的ID:Q74OU3m57c9x 如果你装了IDE工具,可以直接访问上面的demo路径 通过代码片段将demo的ID输入进去也可添加: [图片] [图片] 下面分享下主要的代码内容和“填坑现场”: 一、添加字体 https://developers.weixin.qq.com/miniprogram/dev/api/canvas/font.html [代码]canvasContext.font = value //示例 ctx.font = `normal bold 20px sans-serif`//设置字体大小,默认10 ctx.setTextAlign('left'); ctx.setTextBaseline("top"); ctx.fillText("《智酷方程式》专注研究和分享前端技术", 50, 15, 250)//绘制文本 [代码] 符合 CSS font 语法的 DOMString 字符串,至少需要提供字体大小和字体族名。默认值为 10px sans-serif 文字过长在canvas下换行问题处理(最多两行,超过“…”代替) [代码]ctx.setTextAlign('left'); ctx.setFillStyle('#000');//文字颜色:默认黑色 ctx.font = `normal bold 18px sans-serif`//设置字体大小,默认10 let canvasTitleArray = canvasTitle.split(""); let firstTitle = ""; //第一行字 let secondTitle = ""; //第二行字 for (let i = 0; i < canvasTitleArray.length; i++) { let element = canvasTitleArray[i]; let firstWidth = ctx.measureText(firstTitle).width; //console.log(ctx.measureText(firstTitle).width); if (firstWidth > 260) { let secondWidth = ctx.measureText(secondTitle).width; //第二行字数超过,变为... if (secondWidth > 260) { secondTitle += "..."; break; } else { secondTitle += element; } } else { firstTitle += element; } } //第一行文字 ctx.fillText(firstTitle, 20, 278, 280)//绘制文本 //第二行问题 if (secondTitle) { ctx.fillText(secondTitle, 20, 300, 280)//绘制文本 } [代码] 通过 ctx.measureText 这个方法可以判断文字的宽度,然后进行切割。 (一行字允许宽度为280时,判断需要写小点,比如260) 二、获取临时地址并设置图片 [代码]let mainImg = "https://demo.com/url.jpg"; wx.getImageInfo({ src: mainImg,//服务器返回的图片地址 success: function (res) { //处理图片纵横比例过大或者过小的问题!!! let h = res.height; let w = res.width; let setHeight = 280, //默认源图截取的区域 setWidth = 220; //默认源图截取的区域 if (w / h > 1.5) { setHeight = h; setWidth = parseInt(280 / 220 * h); } else if (w / h < 1) { setWidth = w; setHeight = parseInt(220 / 280 * w); } else { setHeight = h; setWidth = w; }; console.log(setWidth, setHeight) ctx.drawImage(res.path, 0, 0, setWidth, setHeight, 20, 50, 280, 220); ctx.draw(true); }, fail: function (res) { //失败回调 } }); [代码] 在开发过程中如果封面图无法按照约定的比例(280x220)给到: 那么我们就需要处理默认封面图过大或者过小的问题,大致思路是:代码中通过比较纵横比(280/220=1.27)正比例放大或者缩小原图,然后从左上切割,竟可能保证过高的图是宽度100%,过宽的图是高度100%。 在canvas中draw图片,必须是一个(相对)本地路径,我们可以通过将图片保存在本地后生成的临时路径。 微信官方提供两个API: wx.downloadFile(OBJECT)和wx.getImageInfo(OBJECT)。都需先配置download域名才能生效。 三、裁切“圆形”头像画图 [代码]ctx.save(); //保存画图板 ctx.beginPath()//开始创建一个路径 ctx.arc(35, 25, 15, 0, 2 * Math.PI, false)//画一个圆形裁剪区域 ctx.clip()//裁剪 ctx.closePath(); ctx.drawImage(headImageLocal, 20, 10, 30, 30); ctx.draw(true); ctx.restore()//恢复之前保存的绘图上下文 [代码] 使用图形上下文的不带参数的clip()方法来实现Canvas的图像裁剪功能。该方法使用路径来对Canvas话不设置一个裁剪区域。因此,必须先创建好路径。创建完整后,调用clip()方法来设置裁剪区域。 需要注意的是裁剪是对画布进行的,裁切后的画布不能恢复到原来的大小,也就是说画布是越切越小的,要想保证最后仍然能在canvas最初定义的大小下绘图需要注意save()和restore()。画布是先裁切完了再进行绘图。并不一定非要是图片,路径也可以放进去~ 小程序 canvas 裁切BUG [代码]ctx.setFillStyle("#fff"); ctx.fillRect(0, 0, 320, 500); //第一个填充矩形 wx.downloadFile({ url: headUri, success(res) { ctx.beginPath() ctx.arc(50, 50, 25, 0, 2 * Math.PI) ctx.clip() ctx.drawImage(res.tempFilePath, 25, 25); //第二个填充图片 ctx.draw() ctx.restore() ctx.setFillStyle("#fff"); ctx.fillRect(0, 0, 320, 500); ctx.draw(true) ctx.restore() } }) [代码] clip裁切这个功能,如果有超过一张图片/背景叠加,则裁切效果失效。 错误参考:http://html51.com/info-38753-1/ 四、将canvas导出成虚拟地址 [代码]wx.canvasToTempFilePath({ fileType: 'jpg', canvasId: 'customCanvas', success: (res) => { console.log(res.tempFilePath) //为canvas的虚拟地址 } }) res: { errMsg: "canvasToTempFilePath:ok", tempFilePath: "http://tmp/wx02935bb29080a7b4.o6zAJswFAuZuKQ5NZfPr….cGnD1a02PlVC0b3284be3a41d08986c2477579a5fd8e.jpg" } [代码] 这里需要把canvas里面的内容,导出成一个临时地址才能保存在相册,比如: http://tmp/wx02935bb29080a7b4.o6zAJswFAuZuKQ5NZfPr5UfJVR4k.cGnD1a02PlVC0b3284be3a41d08986c2477579a5fd8e.jpg 五、询问并获取访问手机本地相册权限 [代码]wx.getSetting({ success(res) { console.log(res) if (!res.authSetting['scope.writePhotosAlbum']) { //判断权限 wx.authorize({ //获取权限 scope: 'scope.writePhotosAlbum', success() { console.log('授权成功') //转化路径 self.saveImg(); } }) } else { self.saveImg(); } } }) [代码] 判断是否有访问相册的权限,如果没有,则请求权限。 六、保存到用户手机本地相册 [代码]wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: function (data) { wx.showToast({ title: '保存到系统相册成功', icon: 'success', duration: 2000 }) }, fail: function (err) { console.log(err); if (err.errMsg === "saveImageToPhotosAlbum:fail auth deny") { console.log("当初用户拒绝,再次发起授权") wx.openSetting({ success(settingdata) { console.log(settingdata) if (settingdata.authSetting['scope.writePhotosAlbum']) { console.log('获取权限成功,给出再次点击图片保存到相册的提示。') } else { console.log('获取权限失败,给出不给权限就无法正常使用的提示') } } }) } else { wx.showToast({ title: '保存失败', icon: 'none' }); } }, complete(res) { console.log(res); } }) [代码] 保存到本地需要一定的时间,需要加一个loading的状态。 七、关于组件中引用canvas [代码]let ctx = wx.createCanvasContext('posterCanvas',this); //需要加this [代码] 在components中canvas无法选中的问题: 在components自定义组件下,当前组件实例的this,表示在这个自定义组件下查找拥有 canvas-id 的 <canvas> ,如果省略则不在任何自定义组件内查找。
2021-09-13 - canvas 类型为2d时,使用CanvasContext.drawImage与预期的结果不符?
原本使用 var context = wx.createCanvasContext('firstCanvas') 这种方式来使用canvas,并且制作成分享图,得到的图片没有问题。后来优化成使用 2d 的方式来制作图片,ctx.drawImage 画出的图片却有问题 期望的结果: [图片] 得到的结果: [图片] 参数都是一样的,只是原先用的是图片本地路径,2d 用的是一个image, canvas的宽高为375*667: ctx.drawImage(path, 0, 0, 375, 375)
2020-02-19 - 逆地址解析(位置描述)
首页调用逆地址解析 申请配额远远不够,需要每秒五百次以上,求解决方法
2019-04-10