先看效果:
代码:
<template>
<view class="container">
<!-- 左侧fixed导航区域 -->
<view class="left">
<view
v-for="item in leftList"
:key="item.id"
class="left_item"
:class="item.id == selectedId ? 'selected' : ''"
@click="leftItemClick(item.id)"
>
{{ item.title }}
</view>
</view>
<!-- 右侧内容区域 -->
<view class="right">
<view v-for="item in rightList" :key="item.id" class="right_item">
{{ item.content }}
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
leftList: [],
rightList: [],
selectedId: 1, //左边当前选中id
itemTopArr: [], //右侧所有item的top数组
};
},
onLoad() {
this.initData();
this.getItemTopArr();
},
//页面滚动监听
onPageScroll(e) {
let scrollTop = e.scrollTop;
console.log("scrollTop = " + scrollTop);
for (let i = 0; i < this.itemTopArr.length; i++) {
if (scrollTop >= this.itemTopArr[i]) {
this.selectedId = this.rightList[i].parentId;
console.log("selectedId = " + this.selectedId);
}
}
},
methods: {
//左侧item点击
leftItemClick(id) {
this.selectedId = id;
let index = 0;
for (let i = 0; i < this.rightList.length; i++) {
if (this.rightList[i].parentId == id) {
index = i;
break;
}
}
//将页面滚动到目标位置
uni.pageScrollTo({
scrollTop: this.itemTopArr[index],
duration: 300, //滚动动画时长
});
},
//获取右侧所有item的top数组
getItemTopArr() {
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this);
const nodesRef = query.selectAll(".right > .right_item");
nodesRef
.fields(
{
size: true,
rect: true,
scrollOffset: true,
},
(res) => {
res.forEach((item) => {
this.itemTopArr.push(item.top);
});
console.log(this.itemTopArr);
}
)
.exec();
});
},
//初始化数据源
initData() {
for (let index = 1; index < 10; index++) {
for (let i = 1; i < 4; i++) {
this.rightList.push({
id: index + "-" + i,
parentId: index,
content: "content-" + index,
});
}
this.leftList.push({
id: index,
title: "title-" + index,
});
}
},
},
};
</script>
<style lang="scss" scoped>
.container {
position: relative;
min-height: 100vh;
background: #fff;
.left {
position: fixed;
width: 120px;
height: 100%;
min-height: 100vh;
overflow: auto;
float: left;
background: #f2f2f2;
.left_item {
width: 100%;
height: 60px;
text-align: center;
line-height: 60px;
}
.selected {
background: #fff;
font-weight: bold;
color: #07c160;
}
}
.right {
margin-left: 120px;
width: calc(100vw - 120px);
overflow: auto;
.right_item {
width: 100%;
height: 200px;
text-align: center;
line-height: 200px;
font-size: 24px;
border-bottom: 1px solid #ccc;
box-sizing: border-box; //padding、border不影响元素的宽高
}
}
}
</style>
官方onPageScroll方法的使用注意事项(见上图)里说不要在此方法里写复杂的交互,故将onPageScroll里的for循环改写到滚动结束执行:
<template>
<view class="container">
<!-- 左侧fixed导航区域 -->
<view class="left">
<view
v-for="item in leftList"
:key="item.id"
class="left_item"
:class="item.id == selectedId ? 'selected' : ''"
@click="leftItemClick(item.id)"
>
{{ item.title }}
</view>
</view>
<!-- 右侧内容区域 -->
<view class="right">
<view v-for="item in rightList" :key="item.id" class="right_item">
{{ item.content }}
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
leftList: [],
rightList: [],
selectedId: 1, //左边当前选中id
itemTopArr: [], //右侧所有item的top数组
timeoutID: null,
};
},
onLoad() {
this.initData();
this.getItemTopArr();
},
//页面滚动监听
onPageScroll(e) {
let scrollTop = e.scrollTop;
console.log("scrollTop = " + scrollTop);
clearTimeout(this.timeoutID);
this.timeoutID = setTimeout(() => {
console.log("结束滚动");
for (let i = 0; i < this.itemTopArr.length; i++) {
if (scrollTop >= this.itemTopArr[i]) {
this.selectedId = this.rightList[i].parentId;
console.log("selectedId = " + this.selectedId);
}
}
}, 100);
},
methods: {
//左侧item点击
leftItemClick(id) {
this.selectedId = id;
let index = 0;
for (let i = 0; i < this.rightList.length; i++) {
if (this.rightList[i].parentId == id) {
index = i;
break;
}
}
//将页面滚动到目标位置
uni.pageScrollTo({
scrollTop: this.itemTopArr[index],
duration: 300, //滚动动画时长
success: (res) => {
console.log(res);
console.log("scroll success");
},
fail: (err) => {
console.log(err);
console.log("scroll fail");
},
});
},
//获取右侧所有item的top数组
getItemTopArr() {
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this);
const nodesRef = query.selectAll(".right > .right_item");
nodesRef
.fields(
{
size: true,
rect: true,
scrollOffset: true,
},
(res) => {
res.forEach((item) => {
this.itemTopArr.push(item.top);
});
console.log(this.itemTopArr);
}
)
.exec();
});
},
//初始化数据源
initData() {
for (let index = 1; index < 10; index++) {
for (let i = 1; i < 4; i++) {
this.rightList.push({
id: index + "-" + i,
parentId: index,
content: "content-" + index,
});
}
this.leftList.push({
id: index,
title: "title-" + index,
});
}
},
},
};
</script>
<style lang="scss" scoped>
.container {
position: relative;
min-height: 100vh;
background: #fff;
.left {
position: fixed;
width: 120px;
height: 100%;
min-height: 100vh;
overflow: auto;
float: left;
background: #f2f2f2;
.left_item {
width: 100%;
height: 60px;
text-align: center;
line-height: 60px;
}
.selected {
background: #fff;
font-weight: bold;
color: #07c160;
}
}
.right {
margin-left: 120px;
width: calc(100vw - 120px);
overflow: auto;
.right_item {
width: 100%;
height: 200px;
text-align: center;
line-height: 200px;
font-size: 24px;
border-bottom: 1px solid #ccc;
box-sizing: border-box; //padding、border不影响元素的宽高
}
}
}
</style>