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


需求

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

———————————————————————————————————————————————————————————————

实现方法

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

    实现思路:定义一个isFixed类,设置该类的样式为position:

    flex。监听滚动事件,当滚动条到达某个位置时,该元素添加isFixed类。

    问题:如果我们单纯设置这样的类切换时,当我们想要吸顶的元素下面存在其他元素时,因为该组件/元素已经脱离了标准流,那么下面的元素会在吸顶元素脱离标准流的一瞬间往上面跳一下。(还有第二个延迟问题和方法二也会出现,所以在最后说明)

    解决办法:我们可以在需要吸顶的组件下面设置一个样式相同的组件进行占位(该组件只是单纯进行占位),在触发吸顶事件时,使用wx:if控制其出现(hidden和wx:if的区别应该大家都知道。但是因为微信小程序的自定义组件不支持hidden属性,所以如果是自定义组件建议使用wx:if进行控制。如果是系统组件可以使用hidden)

    最后,上代码

———————————————————————————————————————————————————————————————

wxml文件

<block wx:for="{{10}}" wx:key="list">

  <view class="text">我卢本伟没有开挂</view>

</block>

<view class="navbar-wrap">

  <view class="tab-control {{isFixed?'fixed':''}}" id="navbar">

    <view >tab1</view>

    <view >tab2</view>

    <view >tab3</view>

    <view >tab4</view>

  </view>

  <!-- 用于吸顶后占位用的 -->

  <view class="tab-control" wx:if="{{isFixed}}"></view>

</view>

<block wx:for="{{40}}" wx:key="list">

  <view class="text">我卢本伟没有开挂</view>

</block> 

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事件监听的响应速度问题,然后在错误的道路上一去不返。那么到底是什么导致我们屡试不爽的吸顶效果在小程序上会出现这种错误呢?我们看一下官方文档的解释:


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

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

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

//为了防止不停的setData, 这儿做了一个等式判断。 只有处于吸顶的临界值才会不相等

    if (this.data.isFixedTop == isSatisfy) {

      return false;

    }else {

      this.setData({

        isFixedTop: isSatisfy

      });

    }