- 如何彻底解决小程序滚动穿透问题
背景 俗话说,产品有三宝:弹窗、浮层加引导,足以见弹窗在产品同学心目中的地位。对任意一个刚入门的前端同学来说,实现一个模态框基本都可以达到信手拈来的地步,但是,当模态框里边的内容滚动起来以后,就会出现各种各样的让人摸不着头脑的问题,其中,最出名的想必就是滚动穿透。 什么是滚动穿透? 滚动穿透的定义:指我们滑动顶层的弹窗,但效果上却滑动了底层的内容。 具体解决方案分析如下: 改变顶层:从穿透的思路考虑,如果顶层不会穿透过去,那么问题就解决了,所以我们尝试给蒙层加catchtouchmove,但是发现部分场景无效果,那么就不再赘述了。 改变底层:既然是顶层影响了底层,要是底层不会滚动,那就没这个问题了。 如何改变底层解决该问题呢? 不成熟方案: 底部页面最外层view设置position: fixed;页面不可滚动,但是这个时候会导致页面回到顶部。 滚动时监听滚动距离,弹窗时记录滚动位置,关闭弹窗后使用wx.pageScrollTo回滚到记录的位置。 成熟方案 使用page-meta组件,通过该组件我们可以操作Page的style样式,类似于h5里body设置overflow: hidden; 控制页面不可滚动。文档地址:https://developers.weixin.qq.com/miniprogram/dev/component/page-meta.html 使用wx.setPageStyle设置overflow: hidden, 也可以实现给Page组件设置样式。) page-meta组件: 通过该组件我们可以直接操作[代码]Page[代码]组件 ,我们给它的wxss样式overflow动态设置[代码]hidden[代码]or[代码]visible[代码]or[代码]auto[代码] 就可以控制整个页面是否可以滚动。 [图片] wx.setPageStyle方法: 调用这个api,动态设置它为hidden/auto,用于控制页面是否可滚动,主要用于页面组件内使用,比如封装好的弹窗组件,就不用单独写page-meta组件了。。 [代码]wx.setPageStyle({ style: { overflow: 'hidden' // ‘auto’ } }) [代码] 老规矩,结尾放代码片段: https://developers.weixin.qq.com/s/U6ItgQmP7upQ 拓展 支付宝小程序虽然存在page-meta组件,但是由于内核为69版本,给page设置overflow: hidden 也无法控制底部元素不可滚动,目前已联系支付宝的底层开发同学提供API控制页面disableScroll,目前正在封装Appx,近期开放。
08-06 - 使用页面容器
[视频] 你好,我是李艺。上节课我们学习了使用recycle-view组件渲染长列表内容。这节课我们学习使用页面容器。 有时候我们在页面上会弹出一些特定的半屏窗口,例如登录窗口。这时候如果用户在iOS设备上使用了左滑手势或者是在Android设备上单击了物理返回键,会造成页面跳转到上一个页面,这在大多数情况下它并不是用户的真实本意,用户可能只是想将当前弹出的半屏的假页面给它关掉。 为了解决这个矛盾,小程序提供了页面容器组件,使用这个组件,在iOS用户做出左滑手势及Android用户单击物理返回键以及在代码里边调用了wx.navigateBack接口的时候仅是关闭这个容器,它并不会跳出当前这个页面。同时该页面容器还可以放在自定义的组件里面,像一个独立的组件那样去定义和使用。这可以让项目代码结构变得更加的清晰。下面我们看实例演示。 如何使用页面容器及如何自定义组件page-container? 就是这个页面容器组件,它是一个标准组件,它不需要引入就可以使用了,下面我们会创建一个自定义的组件login,在自定义组件里面使用page-container组件,当需要登录的时候,我们让login组件从这个页面的底部划出来然后再取消,或者是完成登录的时候调用自定义组件的close方法将半屏的假页面进行关掉,完成以后这个页面效果大概是像我们现在屏幕上看到的这样一个截图这个样子。 现在我们在屏幕上看到的这个是组件的wxml代码,其中第二行里面有一个position属性它是控制面板从底部划出的。它的show属性控制面板是否显示有两处wxml,这个节点的tap事件绑定到了这个close方法之上,无论是单击到哪一个地方close方法都是会被调用的,它是用于关闭我们当前的登录面板了。我们现在在屏幕上看到的是组件的JS代码,在这个方法close里边先尝试使用了wx.navigateBack然后进行退出。如果当前小程序页面栈里面它有页面调用会成功,反之如果是当前这个页面它是启动页或者是当前这个页面前面它没有页面了则会调用失败。在调用失败的时候也没有关系,可以再尝试使用setData改变数据属性visible,然后以此来关闭我们的一个面板。 现在我们在屏幕上看到的是这个组件的样式代码,这样式代码也没有什么稀奇的地方。主要是为了实现整个组件的一个外观,这个组件的外观它需要向我们用户提示,需要获取它的昵称、头像、性别等等这些信息。这些都是在微信小程序设计规范里边所规定的一些内容。我们需要按照这样的一个规范去设计我们的页面。现在我们在屏幕上看到的是我们组件在使用的时候,配置文件里面的一个配置信息以及在wxml里面的使用的信息,在wxml这个代码里面给了一个登录按钮,添加了一个tap事件监听,如我们现在看到的代码它是事件监听方法showLoginPanel的这样的一个代码。当单击我们登录按钮的时候,它会从底部然后打开我们登录面板。 下面我们进行实践一的代码演示如何使用页面容器及自定义组件。 首先我们需要在我们这个项目里面去实现一个自定义组件,在我们component 就是新的一个目录下,现在这个目录目前还不存在,我们看一下我们最终的,最终源码里边的它的一个实现情况,找到我们的2.3目录,然后这有一个components,这有一个目录,这个里面有一个login的组件。这个组件就是我们要实现的对吧,我们可以将这个目录拷贝一下,然后进到这个目录里面,进到资源管理器里面将整个目录给它拷贝一下,然后回到小程序项目里面,然后在这个地方再打开我们的资源管理器。 然后拷贝到这个地方,这个组件就已经进来了,我们看一下这个login组件我们主要做了什么事情?首先在wxml里面,外层我们使用了一个page-container这样的页面容器组件,然后它有一个show这样的一个属性,用visible然后去控制它的一个显示,后面position等于bottom它是控制它从底部进行滑出,这是它两个重要的属性。里边这些是我们实现的一个界面UI。这个界面UI是参照微信小程序设计规范而设计的,因为规范里面也有提到当我们想向用户去申请授权的时候,我们应该明确地告诉用户我们在申请什么样的一个权限,要获取他的什么样的信息 对吧,所以这个地方我们把这些信息都要给它设计上。 这是这样的一个。然后我们再看一下它的JS代码,这个地方是一个属性然后show属性默认等于false,这地方是一个属性监听。监听show属性如果它变化的时候,我们就设置visible等于它的新值,这是data对象里面visible控制组件的显示。method里面有两个方法一个是这个login,这个login我们是在我们这个里边有一个叫做确认地方。 这个button它有一个特殊的open-type等于getUserInfo,这是我们特殊的一个类型用于获取用户的信息,这个地方有一个事件监听就是catchgetuserinfo等于login绑定到我们这个方法上面,我们可以通过这个事件对象的detail这个信息去间接地去获取userInfo信息。当然这个里面它有其他的一些加密信息,加上这些信息,如果是我们一起传给后端的话那就可以进一步的解密得到 openId及unionId等等这些其他的一些信息了。这个地方我们是简单地将我们userInfo信息取出来,然后复制到我们globalData就是全局的对象上面去,然后以备后面使用,同时后面就调到close,close这个方法首先我们去尝试用navigateBack去调用了一下。 因为这个方法它是有客户端和版本限制的,它其实我们不能保证所有的情况下它都可以调用成功,所以这个地方我们要有一个判断。如果是它这种方式调用不成功的话,我们就用setData这种传统的方式去设置visible等于false来关闭这样的一个登录面板,这是它的一个主要代码。这个组件代码已经有了,至于另外的两个文件是样式,这些其实都没有什么好讲的,都是根据组件的需要然后去实现的。 下面在我们用户主页里面。在my这个文件里面开始去引入这个文件,用usingComponents,然后是login写它的地址,这个地址简单起见我们可以用拷贝大法对吧,先把相对路径拷贝过来然后再稍微地修改一下这样就可以了。引入以后就是使用,在我们wxml的文件的最下方,因为我们想放在页面的最上面所以我们要放在最下方,给它一个loginPanel这样的一个id。这个页面里面有一个登录的一个button,可以找一下在这个地方有登录的button ,所以这个地方给它加一个tap的事件的一个监听,然后在这个事件监听里面,就是我们想单击以后就是弹出我们的登录面板。这个地方加一个showLoginPanel,这是我们绑定的方法,把这个名字拷贝一下然后在我们的这个里边。这个方法我们应该还没有实现对吧,然后在这个里面去添加。 这个方法的一个实现,这个地方我们想去控制我们登录面板的显示一共是有两种方式: 第一种方式是使用数据绑定的这种方式。比如说我们现在搞一个showLogin这样的一个数据属性 默认等于false,然后我们将属性绑定在我们的组件上面,就放在这个地方,它有一个show的属性对不对,我们绑定在这个上面,在想弹出这个面板的时候,只需要用setData去设置showLogin等于true 这样就可以了。代码已经搞完了,现在我们单击编译看一下它的运行表现,然后普通编译我们可以选择我们的用户主页作为起始页面进行测试,然后单击登录,然后我们这个面板出来了对吧,可以取消,单击确认,可以发现我们userInfo信息也已经拿到了,这是一种显示我们面板的一种做法。 另外还有一种做法,更简单一种做法是我们不需要数据属性。不要这个数据属性,然后这个地方这个也不需要绑定。然后只需要拿id 给它一个id在这个地方,我们用页面的selectComponent这个方法,然后将id传给它以后我们就可以调用setData方法了。setData它里面我们还记得它有一个什么样的数据visible对吧,visible等于true 直接调到这个方法visible等于true,我们看一下这个表现,然后单击,也可以对不对。这种方式实现的效果跟我们刚才实现那种实现方式效果是一样的。但是我们这种方式不需要在这个页面里边去定义新的数据属性了,对不对?只需要然后查找这个组件然后调用它的方法就可以了,这种方式更简洁对不对?也不需要在wxml里面去绑、去做数据绑定了。 我们知道我们组件上其实它还有一个属性就是show属性,如果是我们直接去改变show属性,这种方式是不是也可以。比如说我们查到这个组件以后直接改了show等于true,然后上面这个先注掉,然后这样再测试一下,然后单击登录按钮。不行,这种方式不可以,所以我们就用上面这种方式就好了。这种方式是可以的,再测试一下,都可以,在我们面板里面这个取消与下面这个取消,其实它都绑定了close,因为我们用了page-container,因为我们知道它还可以响应什么,可以响应模拟操作对吧。我们比如说单击返回,这个不可以。 代码演示就到这里。 最后我们总结一下使用自定义组件的好处。page-container组件不仅可以响应iOS左滑这样的一个事件以及Android物理返回键这样的一个操作,它还默认实现了从页面的各个方向动画出场的一些效果 。对于开发者而言其实实在没有必要再造轮子实现各种浮窗组件了,类似的需求都可以基于page-container这样的一个页面容器组件然后以自定义组件的方式进行实现。 自定义组件是一种很好的封装页面视图功能的一种方式,它至少有以下优点,我们一起看一下: 第一点它可以复用,可以在各个页面里边使用,不需要重复定义; 第二点它可以将视图样式按组件进行划分,使样式代码更加清晰; 第三点就是它可以缩小setData的一个作用范围,当这个组件的数据发生变化的时候,重渲染机制它仅局限于当前组件内部发生作用,这在一定程度上也提高了视图的一个渲染的效率,现在我们在屏幕上看到的是这节课用到的一些文档地址。 这节课我们就讲到这里。 点击查看文档 这节课我们主要学习了容器组件如何使用,了解了自定义组件的好处我们自定义的login组件将负责接管所有页面的登录请求。这种组件化的开发方式它既让代码更加易于维护也避免了相似登录逻辑的一个重复编写。在一定程度上减少了代码包的一个体积,隐隐之中还提升了这个页面的渲染的效率,可以说是有百利而无一害。 下节课我们学习如何在用户主页页面里面优化动画效果。这里有一个问题请你思考一下:在HTML5页面开发里面有一种响应式设计方式。一种典型的页面效果是这样的:当用户向下滑动页面的时候页面上的元素它以不同的动画方式,从临时状态过渡到最终的目标状态,人类的眼睛对静态元素他感知是不灵敏的,但是对变化的元素的感知却是很灵敏的。使用响应式设计可以显著提升用户对这个页面上指定内容的一个关注度,在这个小程序里面我们怎么样去实现这种响应式的一个设计? 这个问题先留给你思考一下,下节课我们一起来深入探讨一下这个问题。
2022-07-13 - 微信小程序答题页——swiper渲染优化及swiper分页实现
前言 swiper的加载太多问题,网上资料好像没有一个特别明确的,就拿这个答题页,来讲讲我的解决方案 这里实现了如下功能和细节: 保证swiper-item的数量固定,加载大量数据时,大大优化渲染效率记录上次的位置,页面初次加载不一定非得是第一页,可以是任何页答题卡选择某一index回来以后的数据替换,并去掉swiper切换动画,提升交互体验示例动图 [图片] 截图 [图片] [图片] 问题原因 当swiper-item数量很多的时候,会出现性能问题 我实现了一个答题小程序,在一次性加载100个swipe-item的时候,低端手机页面渲染时间达到了2000多ms 也就是说在进入答题页的时候,会卡顿2秒多去加载这100个swiper-item 思考问题 那我们能不能让他先加载一部分,然后滑动以后再去改变item的数据,让swiper一直保持一定量的swiper-item? 注意到官方文档有这么两个属性可以利用,我们可以开启衔接滑动,然后再bindchange方法中去修改data [图片] 1、保证swiper-item的数量固定,加载大量数据时,优化渲染效率 假设我们请求到的数据的为list,实际渲染的数据为swiperList 我们现在给他就固定3个swiper-item,前后滑动的时候去替换数据 正向滑动的时候去替换滑动后的下一页数据,反向滑动的时候去替换滑动后的上一页数据 当我们知道了要替换的条件,我们便可以去替换数据了 但是我们应该考虑到临界值的问题,如果当前页是list第一项和最后一项该怎么办,向左向右滑是不是得禁止啊 这边是判断没数据会让它再弹回去 2、记录上次的位置,页面初次加载不一定非得是第一页,可以是任何页 有很多时候,我们是从某一项直接进来的,比如说上次答题答到了第五题,我这次进来要直接做第六题 那么我们需要去初始化这个swiperList,让它当前页、上一页、下一页都有数据 3、答题卡选择某一index回来以后的数据替换,并去掉swiper切换动画,提升交互体验 从答题卡选择index,那就不仅仅是滑动上下页了,它可以跳转到任何页,所以也采用类似初始化swiperList的方法 swiper切换动画我这边是默认250ms,但是发现有时候从答题卡点击回来,你在答题卡点击的下一项不知道会从左还是从右滑过来 体验真的很差,一开始不知道怎么禁掉动画,其实在跳转到答题卡页的时候把duration设为0就可以了 然后在答题卡页的unload方法中恢复 关键点: 在固定3个swiper-item的同时,要保证我们可以有办法来替代微信自带swiper的current属性和change方法 swiper-limited-load使用方法及说明: 将components中的swiper-limited-load复制到您的项目中在需要的页面引用此组件,并且创建自己的自定义组件item-view在初始化数据时,为你的list的每一项指定index属性具体可以参照项目目录start-swiper-limited-load中的用法说明:其它属性和swiper无异,你们可以自己单独添加你们需要的属性总结 一开始很头疼,为什么微信小程序提供的这个swiper,没去考虑这方面 然后在网上和社区找也没有一个特别好的解决方案。 后来想想,遇到需求就静下来解决吧。 项目地址:https://github.com/pengboboer/swiper-limited-load 如果错误,欢迎指出。 如有新的需求也可以提出来,如果有时间的话,我会帮你们完善。 如果能帮到你们,记得给一个star,谢谢。 ---补充 有很多朋友在评论区提到了分页的需求,抽时间写了一个分页的Demo和大家分享一下。 还是以答题为例,比如我们一共有500条数据,一页20条,可能需要如下功能,乍一看不就加了个分页,挺简单的,其实实现起来挺麻烦的,下面说一下思路和一些需要特别注意的点: 1、从其他页面跳转到答题页时,不光只能默认在第一题,可以是任意一题,比如第80题。 跳转到任意一题,那么需要我们根据index算出该数据在第几页,然后需要请求该页数据,最后显示对应的index。我的思路更注重用户体验,不可能是上滑或者下滑才开始去请求数据,一定是要用户滑动前提前请求好数据。所以起码要保证左右两侧在初始化那一刻都有数据。如果此题和它的上一题下一题都在同一页,那么我们只需要请求一页数据(第15题,那么只需请求第1页数据)。如果此题和它的上一题或者下一题不在同一页,那么我们可能需要请求两页数据。(第20题,那么需要请求第1页和第2页数据) 2、左滑、右滑没数据时,都可以加载新数据。直到滑到第一题或者最后一题。 如果我们初始化时是第24题,那么我们左滑到第21题时,就应该去请求第一页的数据。那么用户在看完21题时,再滑到20题,可能就根本不会感知到通过网络请求了数据。但是如果用户此刻滑动特别快:滑到21题时请求了网络,请求还没成功,就又向左滑了。那么我们需要限制用户的滑动,给用户一个提示:数据正在加载中。 3、从答题卡点击任意一题可以跳转到相应的题目,并且左右滑动显示正常数据 比如我们初始化是跳转到了第80题,不一会点击答题卡又要跳转到200题,一会又跳转到150题。各种无序操作,你也不知道用户要往哪里点。 一开始是想着维护一个主list,点到哪道题往list中添加这道题所在的当页的数据,但是还得判断这一页或者左滑右滑请求新一页的数据得往list的哪个位置添加。这来回来去乱七八糟的判断就很麻烦了,很容易出bug。而且list长度太长了以后insert的性能也不好。 后来就去想,要不答题卡点击任意一题都清空旧的list,然后请求新的数据,左右滑动没数据了再请求新的数据呗。但是这样很浪费资源,并且用户体验也不好,用户已经从第1题答到第200题了,这时用户从答题卡选择了一个25题,还得重新请求网络。而且200道题的数据都没了,那再选个26题,再重新请求网络?网络有延时不说,还浪费资源。 最后转念一想,这时候就需要弄一个缓存了。所以最终的解决方法就出来了:我们维护一个map,在网络请求成功后,在map中保存对应页的数据,同时我们维护一个主list来显示对应的题目。当我们在答题卡选择某一题目,就清空list,然后判断map中有没有该页的数据,如果有就直接拿来,没有就再去网络请求。这个处理方式,写法相对来说简单,不需要乱七八糟的判断,也不浪费资源,用户体验也很不错。 总结 以上就是一些思路和要注意的地方。这个Demo断断续续花了好几天时间写出来的。可能我说的比较啰嗦比较细,只是想让需要用到这个分页Demo的同学能理解我是如何实现的。 如果觉得能帮到你,记得给一个star,谢谢。同时如果这个demo有bug或者你们有新想法,欢迎提出来。
2021-01-07 - 小程序调起付款码相关疑问
小程序调起付款码【支持模式】该功能目前支持直连模式,间连模式只支持从业机构调用,不支持渠道商与机构子进行调用。 【业务交互图】 [图片] 一、开通小程序内拉起微信付款码,这个要走什么流程?解决方案:先找对应BD(服务每个公司对应的腾讯运营同事,如果没有该权限则不支持接入)先申请权限,然后会提供对应文档 二、小程序调起付款码报错“openOfflinePayView:fail”或者“签名错误”,该如何解决?解决方案: 1、 检查是否已经开通权限 (申请成功的邮件进行确认) 2、 检查商户号与小程序之间APPID的绑定关系 商户可登录商户平台,在【产品中心-APPID授权管理】中查看 3、 检查签名大小写 4、 检查是否为小程序的APPID 5、使用签名检查工具: https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=20_1)校验签名算法是否有误 6、 timeStamp类型是String,传入的是数值类型就会出错 7、检查KEY是否正确(别用错了其他商户号的KEY) 8、重置秘钥(支付key) 支付key的位置【账户中心-API安全-APIv2密钥-设置秘钥】 (优先,但是注意重置key对其他线上使用到key的业务是有影响的,这里需要业务量小时再去重置,然后替换所有使用到key的地方),替换重试
2022-07-20 - 微信开放社区正确的提问方式
调侃 某萌新:为什么我在社区提问的问题都没人回答啊 某大佬:社区的问题质量是越来越低了,真不想看社区的问题了 如何提问 今天,在下就来告诉大家如何优雅的提问: 在提问之前,先想好自己遇到的问题是什么,让想要帮助你的人,知道你的问题所在 提问语言简洁明了,详细说明重现步骤及可能存在的问题 附上问题截图,比如调试工具报错信息、提审被驳回信息 写明代码环境,如工具版本、手机版本、基础库版本等 如果调用接口报错,请直接把接口的官方文档地址贴出来,标明调用接口地址,附上请求的参数以及返回的数据 必要时请提供代码截图或代码片段 点击查看如何创建代码片段 最重要的一点:提问时请务必注意礼貌。这一点特别重要,因为这关乎到回答者决定要不要回答以及以怎么样的态度来回答 依然是重要的一点:请务必给认真回答协助解决问题的回答者一个“赞”,这会更加激起他们回答的积极性 千万不要投机,把问题提到“文章”版块,这样会被直接隐藏。不仅不会加快被解答的速度,还会耽误解答机会 示例 (仅为示例,不代表该问题实际存在) 标题:picker组件多级联动会出现选项为 null 的情况? 内容: 使用picker组件,设定为多级联动,仅滚动一级栏后确定,后面的栏目获取到的值为 null,能麻烦帮忙看下吗?最新版开发者工具,基础库版本 2.9.2 代码片段:xxxxxxxxxxxxxxxx 继续调侃 某萌新:诶?真的诶,这样提问,好多问题都被解决了 某大佬:这问题问得是真的好,多来点这样的问题,回答也舒坦
2020-01-03