微信小程序实现tab栏的吸顶效果(解析scroll-view组件scroll事件/onPageScroll事件的延迟问题)

需求

在前端开发中,我们经常会遇到一个需求,在页面进行滚动操作的时候,当页面其中的某个元素滚动到某个位置,我们就让该元素停留在该位置。等到滚动小于某个值的时候,再让该元素继续随着页面滚动。这就是我们平时说的吸顶效果。


实现方法

根据我个人的总结,对于上述需求,我们在大多数情况下会选择使用下面方法来实现这个功能。

  • 实现思路:定义一个isFixed类,设置该类的样式为position:
    flex。监听滚动事件,当滚动条到达某个位置时,该元素添加isFixed类。
  • 问题:如果我们单纯设置这样的类切换时,当我们想要吸顶的元素下面存在其他元素时,因为该组件/元素已经脱离了标准流,那么下面的元素会在吸顶元素脱离标准流的一瞬间往上面跳一下。(还有第二个延迟问题和方法二也会出现,所以在最后说明)
  • 解决办法:我们可以在需要吸顶的组件下面设置一个样式相同的组件进行占位(该组件只是单纯进行占位),在触发吸顶事件时,使用wx:if控制其出现(hidden和wx:if的区别应该大家都知道。但是因为微信小程序的自定义组件不支持hidden属性,所以如果是自定义组件建议使用wx:if进行控制。如果是系统组件可以使用hidden)
  • 最后,上代码

wxml文件


  我卢本伟没有开挂


  
    tab1
    tab2
    tab3
    tab4
  
  
  


  我卢本伟没有开挂


wxss

.navbar-wrap {
  width: 100%;
}
.text {
  background-color: pink;
}
.navbar-wrap .tab-control {
  width: 100%;
  height: 80rpx;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-around;
  background: #fff;
  border-bottom: solid 1px #eee;
  top: 0;
  left: 0;
  z-index: 100;
}
.fixed {
  position: fixed;
}

js

Page({
  data: {
    navbarInitTop: 0,
    isFixed: false, 
  },
  onShow: function() {
  //获取当前组件到顶部的高度
    if (this.data.navbarInitTop == 0) {
      wx.createSelectorQuery().select('#navbar').boundingClientRect((rect) => {
        if (rect && rect.top > 0) {
          var navbarInitTop = rect.top;
          this.data.navbarInitTop = navbarInitTop
        }
      }).exec();
    }
  },
  onPageScroll: function(e) {
    var scrollTop = e.scrollTop;
    var isSatisfy = scrollTop >= this.data.navbarInitTop;
    this.setData({
      isFixed: isSatisfy
    });
  }
})

延迟问题

重点,敲黑板:

如果你直接使用上面的方法,会惊奇的发现,你的tab栏每次都会延迟一点点时间才能拼接上去,根本做不到无缝的吸顶效果。我一开始以为是 onPageScroll事件监听的响应速度问题,然后在错误的道路上一去不返。那么到底是什么导致我们屡试不爽的吸顶效果在小程序上会出现这种错误呢?我们看一下官方文档的解释:

  1. 频繁的去 setData
    在我们分析过的一些案例里,部分小程序会非常频繁(毫秒级)的去setData,其导致了两个后果:
    Android 下用户在滑动时会感觉到卡顿,操作反馈延迟严重,因为 JS 线程一直在编译执行渲染,未能及时将用户操作事件传递到逻辑层,逻辑层亦无法及时将操作处理结果及时传递到视图层;
    渲染有出现延时,由于 WebView 的 JS 线程一直处于忙碌状态,逻辑层到页面层的通信耗时上升,视图层收到的数据消息时距离发出时间已经过去了几百毫秒,渲染的结果并不实时;
  2. 每次 setData 都传递大量新数据
    由setData的底层实现可知,我们的数据传输实际是一次 evaluateJavascript 脚本过程,当数据量过大时会增加脚本的编译执行时间,占用 WebView JS 线程,
  3. 后台态页面进行 setData
    当页面进入后台态(用户不可见),不应该继续去进行setData,后台态页面的渲染用户是无法感受的,另外后台态页面去setData也会抢占前台页面的执行。

事实就是,当我们在监听onPageScroll事件时,我们会一直调用setData方法修改isFixed的值,尽管在这个滚动过程中我们的isSatisfy值并没有改变。而且,官方也说到,在小程序开发中,使用最频繁的也是最容易引起性能问题的接口。所以在以后的开发中,如果遇到这种类似的卡顿的情况,大家可以先检查一下自己的this.setData方法。

说了这么多,解决方案呢?

其实只要我们想到了出现这个问题的原因,那么解决就是很简单了。

    //为了防止不停的setData, 这儿做了一个等式判断。 只有处于吸顶的临界值才会不相等
    if (this.data.isFixedTop == isSatisfy) {
      return false;
    }else {
      this.setData({
        isFixedTop: isSatisfy
      });
    }

这是我在写小程序时自己遇到的最麻烦的问题,现在插眼留在这里,希望能对你有用。如果那里不对请大佬指正。

你可能感兴趣的:(微信小程序实现tab栏的吸顶效果(解析scroll-view组件scroll事件/onPageScroll事件的延迟问题))