第一步,页面布局
{{item.name}}
{{item.name}}
{{item1.name}}
第二步:css部分,这里就自己写了,按照自己的项目效果图自己调一下效果
第三步:js部分
1、
data() {
return {
scrollTop: 0, //tab标题的滚动条位置
oldScrollTop: 0, // tab标题的滚动条旧位置
current: 0, // 预设当前项的值
menuHeight: 0, // 左边菜单的高度
menuItemHeight: 0, // 左边菜单item的高度
itemId: '', // 栏目右边scroll-view用于滚动的id
scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
arr: [], // 储存距离顶部高度的数组
timer: null, // 定时器
flist: [], //左侧一级分类
slist: [] //右侧一级+二级分类,构造可以嵌套的数组结构
}
},
2、点击左侧分类事件
/**
* 点击左边的栏目切换
* @index 传入的 ID
*/
async swichMenu(index) {
if(this.arr.length == 0) {
await this.getMenuItemTop();
}
if (index == this.current) return;
this.scrollRightTop = this.oldScrollTop;
this.$nextTick(function(){
this.scrollRightTop = this.arr[index];
this.current = index;
this.leftMenuStatus(index);
})
},
3、右侧滑动事件
async rightScroll(e) {
this.oldScrollTop = e.detail.scrollTop;
if(this.arr.length == 0) {
await this.getMenuItemTop();
}
if(this.timer) return ;
if(!this.menuHeight) {
await this.getElRect('menu-scroll-view', 'menuHeight');
}
setTimeout(() => { // 节流
this.timer = null;
// scrollHeight为右边菜单垂直中点位置
// let scrollHeight = e.detail.scrollTop + this.menuHeight / 2;
// scrollHeight为右边菜单头部位置
let scrollHeight = e.detail.scrollTop + 20;
for (let i = 0; i < this.arr.length; i++) {
let height1 = this.arr[i];
let height2 = this.arr[i + 1];
if (!height2 || scrollHeight >= height1 && scrollHeight < height2) {
this.leftMenuStatus(i);
return ;
}
}
}, 10)
},
4、页面初始化时,获取右边每个节点距离顶部的高度,主要用于判断是否滑动,比如右边数据比较少的时候就不会在滑动
onReady() {
this.getMenuItemTop()
},
getMenuItemTop() {
new Promise(resolve => {
let selectorQuery = uni.createSelectorQuery();
selectorQuery.selectAll('.class-item').boundingClientRect((rects) => {
// 如果节点尚未生成,rects值为[](因为用selectAll,所以返回的是数组),循环调用执行
if(!rects.length) {
setTimeout(() => {
this.getMenuItemTop();
}, 10);
return ;
}
rects.forEach((rect) => {
// 视情况而定,这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
// this.arr.push(rect.top - rects[0].top);
this.arr.push(rect.top)
resolve();
})
}).exec()
})
},
5、基础功能函数
/**
* 观测元素相交状态
* 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
* 如果跟.right-box底部相交,就动态设置左边栏目的活动状态
*/
async observer() {
this.tabbar.map((val, index) => {
let observer = uni.createIntersectionObserver(this);
observer.relativeTo('.right-box', {
top: 0
}).observe('#item' + index, res => {
if (res.intersectionRatio > 0) {
let id = res.id.substring(4);
this.leftMenuStatus(id);
}
})
})
},
/**
* 设置左边菜单的滚动状态
* @index 传入的 ID
*/
async leftMenuStatus(index) {
this.current = index;
// 如果为0,意味着尚未初始化
if (this.menuHeight == 0 || this.menuItemHeight == 0) {
await this.getElRect('menu-scroll-view', 'menuHeight');
await this.getElRect('u-tab-item', 'menuItemHeight');
}
// 将菜单活动item垂直居中
this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
},
/**
* 获取一个目标元素的高度
* @elClass 元素的类名
* @dataVal 储存高度的对象
*/
getElRect(elClass, dataVal) {
new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(this);
query.select('.' + elClass).fields({
size: true
}, res => {
// 如果节点尚未生成,res值为null,循环调用执行
if (!res) {
setTimeout(() => {
this.getElRect(elClass);
}, 10);
return;
}
this[dataVal] = res.height;
resolve();
}).exec();
})
},
主要是uniapp操作dom节点的时候不是很方便,核心的实现原理其实跟jq一样