uni app锚点定位 、自动吸顶、滚动自动选择对应的锚点

3,391 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

最近产品提了一个新的需求,需要在已有的小程序页面加上一个自动吸顶+锚点的功能,之前虽然做过类似的,但是又有点不相同, 所以此次写下文章记录,效果类似于淘宝的详情页面

1.效果如下

QQ20221010-235050-HD.gif

2.主要的思路

  1. 头部吸顶的按照原始的 sticky 进行布局
  2. 头部的tabs在滚动中显示,需要去监听当前页面的滚动事件,在超出头部的间距后,计算当前的scrollTop在那个区间,然后显示那个tabs选中
  3. 在切换的tabs的时候,滚动到指定的位置

步骤1: 原始的sticky布局

// html部分
<view class="tabs" id="tabs" :style="{'display': showTabs ? 'block': 'none'}">
    <u-tabs
    :current="currentTab"
    :list="tabs"
    @click="clickItem"
    lineColor="#00BB84"
    lineWidth="46rpx"
    lineHeight="10"
    class="tabsStyle"
    :inactiveStyle="{
            color: '#666666',
            fontSize: '30rpx',
            fontWeight: 400
    }"
    :activeStyle="{
            color: '#333333',
            fontSize: '30rpx',
            fontWeight: 'bold'
    }"
    ></u-tabs>
</view>

// css部分
.tabs{
    position: sticky;
    z-index: 970;
    top: 0px;
    background-color: #fff;
    width: 100vw;
    .tabsStyle{
            box-shadow: 0 2rpx 6rpx 0 rgba(153,153,153,0.2);
            ::v-deep{
                    .u-tabs{
                            box-shadow: 0px 4px 6px 0 rgba(153,153,153,0.2);
                    }
            }
    }
    }

步骤2:在滚动事件根据scrollTop值动态的去计算那个tab被checked

12.jpeg

计算选中了那个tab.png

// 1.利用uni app的boundingClientRect
onShow() {
    this.getDistanceArr()
},
// 监听页面滚动
onPageScroll (event) {
    const _this = this
    if (this.isTabChange) {
            return
    }
    const { scrollTop } = event;
    // 偏移量,由于吸顶的tab、头部的显示信息也有高度,素以做了偏移量
    const skewY = 55 
    if (scrollTop >= skewY) {
        // 在未显示tab并且 currentTab <= 0时,防止uview ui抖动bug,设置默认复位值
        if (!this.showTabs && this.currentTab <= 0) { 
                this.currentTab = 0
        }
        this.showTabs = true
        this.$nextTick(() => {
            const length = this.distanceArr.length
            const index = this.distanceArr.findIndex(el => el - skewY - scrollTop > 0)
            // 当index  == -1 的时候,实际当前滚动的距离超出了最大值,也就是在最后一个tab显示的内容
            // 当index > 0 的时候,说明能在当前的scrollTop值找到,即index的前一位
            this.currentTab = index > 0 ? index - 1 : length - 1
        })
    } else {
        this.showTabs = false
    }
},
methods: {
    // 获取所有元素在当前页面所处的位置信息
    getDistanceArr () {
        const _this = this
        _this.tabs.map(el => {
            uni.createSelectorQuery().select(el.id).boundingClientRect(data => {
                // 获取当前ID距离顶部的top值
                _this.distanceArr.push(data.top)
            }).exec()
        })
    },
}

3.切换tabs,页面滚动到指定位置

滚动的距离 = 计算选中Id距离顶部的距离 - 当前外层的容器于顶部的距离 + (或者 - ) skewY (偏移量)

clickItem(item, index) {
    this.isTabChange = true
    const _this = this
    // this.$nextTick 保证当前isTabChange 为true后执行代码
    // 避免在istabChange变为true的时候,执行代码,监听事件还是会继续执行重新计算currenTab值
    this.$nextTick(() => {
        _this.currentTab = item.index
        uni.createSelectorQuery().select(item.id).boundingClientRect(data => {
            uni.createSelectorQuery().select('.wrapper').boundingClientRect(res => {
                const scrollTop = data.top - res.top // 获取差值
                const skewY = 50 // 偏移
                // 页面开始进行滚动到目标位置
                uni.pageScrollTo({
                    // scrollTop的计算需要注意,在往上或者是往下拉的时候 需要加减 吸顶的高度
                    scrollTop: scrollTop > 0 ? scrollTop - skewY :  scrollTop + skewY,
                    duration: 300,
                    complete: function () {
                        const timer = setTimeout(() => {
                            _this.isTabChange = false // 关闭
                            clearTimeout(timer)
                        }, 500)
                        // 真机在测试<500ms的时候,ios无问题,但是安卓和鸿蒙都会在变为false的时候,
                        //再次触发onPageScroll事件,避免兼容性问题,将值改为500ms,
                        //解决ios和安卓、鸿蒙系统兼容性问题
                    }
                });
            }).exec()
        }).exec()
    })
},

优化总结

在点击tab的时候,发现当页面的内容有多个不足以撑满屏幕,比如(法务部门、人事部门这个两个tab的时候)(见下图),在前几个tab的被选中,再次点击最后两个tab的时候,会发现最后两个tab在选中的瞬间又会回到运营部门,我回想了问题可能出现在哪里,一想肯定是onPageScroll事件重新触发了,所以我在点击tab的时候,设置了 isTabChange的boolean值,在完成页面滚动的完成事件之后再次设置isTabChange == false,这样避免在这个期间,受到页面监听滚动事件的影响。由于安卓机器在complete方法里面不设置延迟时间,就会发生抖动问题,故为了解决此问题,设置了延迟。

tab内容比较少.jpg

源码链接

gitee.com/crazyu/uni-…

总结

以上便是我完成整个tab吸顶,页面滚动自动选中tab、滚动到一定距离才显示tab的全部过程,实际开发了4个多小时,安卓的bug,让我查了将近一天的时间,个人猜测可能是安卓的内核延迟机制上略微相对于ios来说要差一些,才导致此问题的出现,如果有其他人知道此问题根源,也可以分享,哈哈,

写在最后

我是crazyu,一位前端开发工程师。

  • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注😊
  • 本文首发于掘金,未经许可禁止转载
  • 本人开始连载微信公众号:crazyu 前端 ,欢迎各位关注