uni app锚点定位 、自动吸顶、滚动自动选择对应的锚点
最近产品提了一个新的需求,需要在已有的小程序页面加上一个自动吸顶+锚点的功能,之前虽然做过类似的,但是又有点不相同, 所以此次写下文章记录,效果类似于淘宝的详情页面
1.效果如下
2.主要的思路
- 头部吸顶的按照原始的 sticky 进行布局
- 头部的tabs在滚动中显示,需要去监听当前页面的滚动事件,在超出头部的间距后,计算当前的scrollTop在那个区间,然后显示那个tabs选中
- 在切换的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
// 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吸顶,页面滚动自动选中tab、滚动到一定距离才显示tab的全部过程,实际开发了4个多小时,安卓的bug,让我查了将近一天的时间,个人猜测可能是安卓的内核延迟机制上略微相对于ios来说要差一些,才导致此问题的出现,如果有其他人知道此问题根源,也可以分享,哈哈,
写在最后
我是crazyu,一位前端开发工程师。
转载自:https://juejin.cn/post/7153085070017265678