当前位置: 首页 > Web前端 > HTML

微信小程序仿instagram交互实现(带长列表优化处理)

时间:2023-03-28 19:57:49 HTML

需求最近几天忙于公司项目的一个新需求。原因是这样的:公司要开发一款偏向社交娱乐的小程序,可以在首页看到用户发的话题帖,每条帖子至少包含一张图片或者一段视频,然后是产品方希望首页能够实现instagram的交互效果,效果图如下:嗯,大致就是这个需求的背景,然后每个post的高度不定,大概500~600px.当我第一次接到这个请求实现思路的时候,其实是有点慌的。毕竟有一段时间没接触小程序了,不知道小程序和文档更新到什么程度了。仔细分析项目需求,大致可以分为两类:交互和性能优化。性能优化因为首页是一个很长的列表,众所周知,一旦页面渲染节点过多,就会卡顿,更何况是小程序,小程序分为逻辑层和渲染层,两者是通过setData联系起来的,所以处理时需要注意两点:setData中的数据量不能太大。我记得好像有大小限制。忘记多少了,懒得去找:clown_face:。你可以在官方文档中找到它;页面可以渲染的帖子数量是有限制的,这里我控制最多渲染25个帖子。处理长列表的优化,官方组件-recycle-view也有对应的组件,不过好像不太符合项目要求,就通过了。虽然没有使用官方组件,但是对于长列表的性能优化在组件文档中有说明。以下是关键点的摘录:核心思想是只渲染屏幕上显示的数据。基本实现是监听滚动事件,重新计算需要渲染的数据,为不需要渲染的数据留一个空的div占位元素。其实就是设置一个变量来控制数据是否可以渲染。如果无法渲染,那么我们将用空视图替换它。需要注意的是,失败的是:空框的高度需要设置为post的高度。这样屏幕就不会闪烁了。针对这种思路,我们可以确定长列表性能优化的解决方案之一:将数据分成二维数组,这样可以限制每次数据setData的数量,数据渲染完成后,获取每组数据占用的总高度,这里的高度是设置数据打乱不渲染时占位框的高度。/***获取已经渲染的组的高度,但是还没有获取到高度*/_getGroupListHeight(){this.data.list.forEach((item,index)=>{if(item.show&&!item.height){constid='XXXXXXXX'//groupidletquery=wx.createSelectorQuery()query.select(id).boundingClientRect(rect=>{this.data.list[index].height=rect.height}).exec();this.getTopicHeight(item.data,index)//获取列表中每个topic的高度,用于计算滑动时的滚动距离}})}上面是一个简单的获取每组高度的代码例如,当该组数据已经渲染完成,但高度未知时,就会获取。增加一个判断步骤的目的是为了防止获取到的组的数据被再次检索,造成不必要的浪费。在获取每组数据的高度时,也会对应的获取组内每条帖子的高度,为后面的仿instagram交互做准备。获取到每组数据的高度后,我们就可以通过监听页面滚动的高度来控制要渲染的数据。需要注意的一点是,我们需要根据这组的数据来渲染上组和下组。两组数据,目的是防止用户快速滑动时白屏的不友好体验。当然你也可以根据自己的需要渲染几组。/***页面滚动*@parame*/onPageScroll(e){//android页面滑动处理(非instagram版)if(!this.data.isIos&&!this.data.scrollBoxInfo.canUseScrollBox){//1.处理当前播放的视频if(this.data.currentPlayingId&&Math.abs(e.scrollTop-this.data.scrollTop)>100){this.selectComponent(this.data.currentPlayingId).pauseVideo()this.data.currentPlayingId=''}//2.处理Andorid渲染的包数据this._dealAndroidScroll(e)//3.处理视频自动播放if(this.timer){clearTimeout(this.timer)}this.timer=setTimeout(()=>{this.data.scrollTop=e.scrollTop//记录当前滚动距离,滑动暂停视频播放时需要this.handleAutoPlay(e)//视频自动播放},300)}}/***Android监控滚动,动态设置组*@param{Object}e*/_dealAndroidScroll(e){letmax_height=0//maximumheightfor(leti=0;i<=this.data.topicScroll.show_index;i++){max_height+=this.data.list[i].height}让min_height=max_height-this.data.list[this.data.topicScroll.show_index].height//最小高度//超出,+1if(e.scrollTop>max_height&&this.data.topicScroll.show_index0//手指滑动的方向,true表示向上滑动,false表示向下滑动constscrollInfo=this.data.topicScroll//1.第一个节点滑动fingerdown&&最后一个节点向上滑动手指没有任何操作if(!scrollInfo.parent_index&&!scrollInfo.child_index&&!direction){return}//第一个节点向下滑动什么都不做.data.list.length-1)&&scrollInfo.child_index===(this.data.list[scrollInfo.parent_index].data.length-1)&&direction){return}//最后一个节点向上滑动,没有任何动作//2.根据滑动的方向,判断哪个节点需要滚动constcan_move=(diffTime<100&&diffY>50)||(diffTime>=100&&diffY>80)//是否可以滑动,手势滑动是根据if(can_move){if(direction){if(scrollInfo.child_index===4){++scrollInfo.parent_indexscrollInfo.child_index=0}else{++scrollInfo.child_index}}else{if(scrollInfo.child_index===0){--scrollInfo.parent_indexscrollInfo.child_index=4}else{--scrollInfo.child_index}}}//3.处理滚动this._dealScroll(can_move?pausePlayId:'',can_move)}根据监听到的信息可以判断是否切换,切换到帖子的操作,所以我们只需要计算高度即可根据获取到的帖子的高度进行滚动按照第一点的操作,我们可以实现模仿Instagram的交互效果,但是忽略了致命点:页面的惯性滚动。也正是因为这个所谓的“惯性滚动”,我才多花几天时间研究交互的实现。众所周知,为了让页面的滑动更加流畅,当我们停止滑动的时候,页面会产生惯性。自动滑动一定距离后停止。Android默认有惯性滚动,而iOS需要额外设置-webkit-overflow-scrolling:touch样式。第一种方案的前提是页面不能有惯性滚动,否则页面无法准确滚动到指定位置。解决方法ios的解决方法比较简单,我们只需要设置-webkit-overflow-scrolling:auto的样式即可。比较麻烦的是android的实现。一开始在网上找了很多资料实现取消页面的惯性滚动。毕竟,页面滚动的性能是最好的。但很遗憾,我并没有找到可行的方案,所以我选择了倒退,模拟手指的滑动。浏览小程序文档后,我选择了两种可能的解决方案:①使用wxs实现手指滑动的效果;②利用scroll-view的快减速属性。显然使用wxs的方案比较复杂,所以一开始选择了scroll-view的方案。Android实现思路scroll-view方案的思路和ios类似。不同的是页面的滚动,因为ios可以直接使用页面滚动,即wx.pageScrollTo,而scroll-view使用scroll-into-view或者scroll-top,为了减少操作,我直接使用scroll-顶级财产。正当我信心满满的时候,我做出来的效果却打了我的脸:每次滚动到指定位置,我都会抖动一下。虽然我没有仔细找出原因,但我猜还是有一定程度的页面滚动。惯性滚动,当我们设置scroll-top的时候,惯性滚动的存在会让列表发生偏移,最后偏移回来,看起来像晃动一样。就在我准备放弃scroll-view方案的时候,无意中发现了scroll-view的api接口。本着尝试新接口的精神,我尝试使用官方的api接口来控制scroll-view滚动。太棒了是的,完成了!!!!由于scroll-view做出来的效果勉强可以,后面就直接通过wxs方案了,毕竟那一块的逻辑要复杂一些。因为当时使用的是scroll-view的api接口,支持的版本库比较高,是2.14.4。虽然大部分用户都可以支持,但是还是需要兼容小部分用户,所以我做了一个scroll-view版和平滑滚动版fast-deceleration的特性,在开发者工具里好像没有效果,但是手机预览是可能的。不知道是开发工具的问题,还是只兼容部分手机(我这里测试了小米和华为,这两款就没有更多的手机测试了:clown_face:)demo里有说这么多,而且可能没有人能够看到它。该演示在最后展示。在调试这个效果的时候,遇到了很多坑,其中一个坑印象很深刻,所以记录在这里:touch时touchstart的target节点被移除了,相应的touchend事件会因为没有target节点而丢失。遇到这个坑是因为小程序的同一个页面不允许存在多个视频,所以需要用图片替换未播放的视频,需要播放视频的时候再替换。这样就会出现上面的情况,导致监听不到touchend事件,整个列表停在原地,没有滚动到指定位置。原链接传送门