在vite模板中使用uni.createSelectorQuery().in(this)来获取节点信息时,由于this不存在,需要找一个替代方法传进去。
import { getCurrentInstance, reactive } from 'vue';
const instance = getCurrentInstance();
const getItemDistence = () => {
letselectQuery = uni.createSelectorQuery().in(instance);
selectQuery.selectAll('.menu-item').boundingClientRect((rect:any)=>{
if(!rect.length){
setTimeout(()=>{
getItemDistence();
},10);
return;
}
rect.forEach((it:any)=>{
state.itemArr.push(it); // 这里获取到的数据是每个item距离页面顶部的数据,以及每个item的自身数据
})
}).exec()
}
uni.createSelectorQuery().in(this)用来实现滚动菜单效果,默认菜单是存在数据的情况,所以不会存在死循环的。
html部分
<view class="container-wrap">
<view class="left" v-if="state.navBarLeftIcon !== 'grid'">
<scroll-view
class="fill"
scroll-y="true"
scroll-with-animation
:scroll-into-view="state.clickId">
<view
v-for="(item,index) in state.dataList"
:key="index+'left'"
:id="'left'+index"
class="left-item"
:class="state.picked == index ? 'checkedStyle' : ''"
@click="selectActiveEvt(index)">
{{item.name}}
</view>
</scroll-view>
</view>
<view class="right">
<scroll-view
class="fill"
scroll-y="true"
scroll-with-animation
:scroll-into-view="state.clickId"
@scroll="scrollEvt">
<view class="menu-item" v-for="(item,index) in state.dataList" :key="index+'right'">
<uni-section :title="item.name" type="line" :titleFontSize="'30rpx'" :id="'left' + index">
<up-grid :col="state.navBarLeftIcon !== 'grid' ? 3 : 4" :border="false">
<up-grid-item
v-for="(menu,menuIndex) in item.children"
:index="menuIndex"
:key="'item' + menuIndex"
@click="goPath(menu.pageUrl)">
<view class="grid-item-box mb-30">
<image
class="image"
:src="menu.icon ? fileUrl + JSON.parse(menu.icon)[0].url : ''"
mode="aspectFill" />
<text class="text">{{menu.name}}</text>
</view>
</up-grid-item>
</up-grid>
</uni-section>
</view>
</scroll-view>
</view>
</view>
css
.container-wrap {
display: flex;
}
.left {
width: 25%;
height: 100%;
margin-right: 10rpx;
background-color: #ffffff;
.left-item {
height: 90rpx;
box-sizing: border-box;
text-align: center;
line-height: 90rpx;
color: black;
font-weight: 600;
font-size: 28rpx;
}
.checkedStyle{
background-color: #eef3f7;
border-left: 4rpx solid #007AFF;
box-sizing: border-box;
color: #007AFF;
}
}
.right {
background-color: #fff;
flex: 1;
height: 100%;
}
.grid-item-box {
display: flex;
flex-direction: column;
align-items: center;
.image {
width: 50rpx;
height: 50rpx;
}
.text {
font-size: 28rpx;
margin-top: 10rpx;
}
}
ts代码
import { getCurrentInstance, reactive } from 'vue';
import { onLoad, onShow } from "@dcloudio/uni-app";
import sendRequest from '@/utils/sendRequest';
import { jumpPage } from '@/utils/tools';
import base from '@/utils/base';
const fileUrl = base.fileUrl;
const state = reactive({
navBarLeftIcon: 'list' as string,
showPopup: false,
dataList: [] as any [], // 列表数据
clickId: 'left0', // 点击选项的id
picked: 0, // 左侧选中index
nowRightIndex: 0, // 右边当前滚动的index
itemArr: [] as any, // 用于存放右侧item位置数据
timer: null as any,
isScroll: true,
});
const instance = getCurrentInstance();
onLoad((options) => {
setTimeout(()=>{
getItemDistance();
},1000)
});
onShow(() => {
state.dataList = uni.getStorageSync('menuList');
});
// 计算右侧每个item到顶部的距离,存放到数组
const getItemDistance = () => {
let selectQuery = uni.createSelectorQuery().in(instance);
selectQuery.selectAll('.menu-item').boundingClientRect((rect:any)=>{
if(!rect.length){
setTimeout(()=>{
getItemDistance();
},10);
return;
}
rect.forEach((it:any)=>{
state.itemArr.push(it); // 这里获取到的数据是每个item距离页面顶部的数据,以及每个item的自身数据
})
}).exec()
}
// 节流
const scrollEvt = (e:any) =>{
if(state.timer){
clearTimeout(state.timer);
}
if(!state.isScroll) {
state.isScroll = true;
return;
}
state.timer = setTimeout(()=>{
// 判断当前顶部是处于哪个item,获取当前item的index
let i:number = -1;
state.itemArr.map((it:any, index:number) => {
if(e.detail.scrollTop >= (it.bottom - 412)) {
i = index;
}
});
state.nowRightIndex = i > -1 ? i : 0;
if(state.nowRightIndex == -1) state.nowRightIndex = 0;
if(state.nowRightIndex == state.picked) return;
if(state.isScroll) {
state.isScroll = false;
state.clickId = 'left' + state.nowRightIndex;
state.picked = state.nowRightIndex;
}
},100);
}
// 选中左侧菜单
const selectActiveEvt = (index:number) => {
state.clickId = 'left' + index;
state.picked = index;
state.isScroll = false;
};
// 选中功能条目
const slectItem = (type: string) => {
console.log(type);
state.showPopup = false;
};
// 页面跳转
const goPath = (path:string) => {
jumpPage(path, 'navigateTo');
}
// 点击左侧区域
const leftClick = () => {
if(state.navBarLeftIcon === 'grid') {
state.navBarLeftIcon = 'list';
} else {
state.navBarLeftIcon = 'grid';
}
};
// 点击右侧区域
const rightClick = () => {
state.showPopup = !state.showPopup;
};
实现效果: