Bootstrap

uniapp代码 --商城详情页,导航随着页面的滚动自动跳转,点击导航项跳转至相应位置

呈现效果

在这里插入图片描述

说明:实现导航与滑动内容之间的双向绑定,页面滑动时导航选中项会自动进行切换、当切换导航选中项,滑动内容会跳转至相应位置;

操作步骤

1.页面渲染,设置导航部分与滑动盒子;
<template>
	<view class="detail">
        <!-- 导航部分 -->
		<view class="nav_box">
			<view class="nav_item">{{item.title}} </view>
		</view>
		</view>
		 <!-- 滑动内容部分 -->
		<scroll-view ref="scrollView" scroll-y="true"  class="scroll_box" >
			<view id="cp"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} cp</view>
			<view id="cb"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} cb</view>
			
			<view id="pj" ></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} pj</view>
			
			<view id="yy"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} yy</view>
			
			<view id="sg"></view>
				<view class="item" v-for="item,index in 50" :key="index">{{item}} sg</view>
			
			
		</scroll-view>
		
	</view>
</template>
<script setup>
import { ref } from "vue"
	// 导航列表
	const navList = ref([
		{
			id:1,
			title:"产品",
		},
		{
			id:2,
			title:"成本",
		},
		{
			id:3,
			title:"评论",
		},
		{
			id:4,
			title:"运营",
		},
		{
			id:5,
			title:"事故"
		},
	])
	</script>

<style lang="scss" scoped>
	.detail{
		.top{
			display: flex;
			align-items: center;
			padding: 0 30rpx;
			height: 100rpx;
			.search{
				flex: 1;
			}
			.top_icon{
				width: 60rpx;
				height: 60rpx;
				padding: 20rpx;
				margin-left: 20rpx;
				image{
					width: 100%;
					height: 100%;
				}
			}
			.share{
				width: 50rpx;
				height: 50rpx;
			}
		}
		.nav_box{
			position: fixed;
			left: 0;
			right: 0;
			display: flex;
			justify-content: space-around;
			align-items: center;
			height: 100rpx;
			padding: 0 30rpx;
			box-shadow: 5rpx 5rpx 10rpx rgba(0, 0,0, 0.5);
			background-color: #fff;
			z-index: 10;
			.nav_item{
				padding: 20rpx;
				// background-color: pink;
				
			}
			.active{
				color: $theme-color;
			}
		}
		.scroll_box{
			background-color: pink;
		}
	}

</style>
2.获取滚动高度;

通过uni.getSystemInfoSync()获取页面信息来动态设置导航盒子的高度与位置、获取的区域

<template>
    <view class="detail">
		<view class="nav_box" id="nav_box" :style="{top:nav_height + 'px'}">
			<view class="nav_item" :class="{'active':index == currentIndex}" v-for="item,index in navList" :key="index" >{{item.title}} </view>
		</view>
		<view class="fill" :style="{height:nav_box_height + 'px'}">
			
		</view>
		<scroll-view ref="scrollView" scroll-y="true" class="scroll_box" :style="{height: window_height + 'px'}" >
			<view id="cp"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} cp</view>
			<view id="cb"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} cb</view>
			
			<view id="pj" ></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} pj</view>
			
			<view id="yy"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} yy</view>
			
			<view id="sg"></view>
				<view class="item" v-for="item,index in 50" :key="index">{{item}} sg</view>
			
			
		</scroll-view>
		
	</view>
</template>
<script setup>
	import { ref,onMounted } from "vue"
	
	
	const window_height = ref(0)// 视图可用高度
	const nav_height = ref(0)//头部导航条高度
	const nav_box_height = ref(0)// 导航高度 
	onMounted(()=>{
		// 设置导航头部高度、导航高度
		let  SYSTEM_INFO =  uni.getSystemInfoSync()
		const nav_box = document.getElementById("nav_box").offsetHeight
		nav_box_height.value = nav_box
		window_height.value = SYSTEM_INFO.windowHeight - nav_box
		nav_height.value = SYSTEM_INFO.windowTop
		
	})
	</script>

3.获取滚动各节点距离滑动的位置;
通过uni.createSelectorQuery()获取滑动各节点位置

<script setup>
	import { onLoad, } from "@dcloudio/uni-app"
	import { ref,onMounted } from "vue"
   // 各部分距离头部的高度
	const scrollView = ref(null)
	const cpNode = ref(null)
	const cbNode = ref(null)
	const pjNode = ref(null)
	const yyNode = ref(null)
	const sgNode = ref(null)
    onMounted(()=>{
		// 获取滑动各个子节点的滑动高度
		const query = uni.createSelectorQuery().in(scrollView.value)
		query.select('#cp').boundingClientRect((rect)=>{
			cpNode.value = rect.top
		}).exec();
		query.select('#cb').boundingClientRect((rect)=>{
			cbNode.value = rect.top
		}).exec();
		query.select('#pj').boundingClientRect((rect)=>{
			pjNode.value = rect.top
		}).exec();
		query.select('#yy').boundingClientRect((rect)=>{
			yyNode.value = rect.top
		}).exec();
		query.select('#sg').boundingClientRect((rect)=>{
			sgNode.value = rect.top
		}).exec();
	})
</script>

4.在scroll-view中的scroll事件中,设置导航选中项;

      <view class="nav_box" id="nav_box" :style="{top:nav_height + 'px'}">
			<view class="nav_item" :class="{'active':index == currentIndex}" v-for="item,index in navList" :key="index" >{{item.title}} </view>
		</view>
		<view class="fill" :style="{height:nav_box_height + 'px'}">
			
		</view>
       <scroll-view ref="scrollView" scroll-y="true" :scroll-top="scroll_top" class="scroll_box" :style="{height: window_height + 'px'}" @scroll="scrollHandle">
			<view id="cp"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} cp</view>
			<view id="cb"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} cb</view>
			
			<view id="pj" ></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} pj</view>
			
			<view id="yy"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} yy</view>
			
			<view id="sg"></view>
				<view class="item" v-for="item,index in 50" :key="index">{{item}} sg</view>
			
			
		</scroll-view>
const currentIndex = ref(0)// 选中导航
 const scrollHandle = (e) => {
		let scrollTop = e.detail.scrollTop
		if(scrollTop>=cpNode.value && scrollTop<cbNode.value){
			currentIndex.value = 0
		}else if(scrollTop>=cbNode.value && scrollTop<pjNode.value){
			currentIndex.value = 1
		}else if(scrollTop>=pjNode.value && scrollTop<yyNode.value){
			currentIndex.value = 2
		}else if(scrollTop>=yyNode.value && scrollTop<sgNode.value){
			currentIndex.value = 3
		}else{
			currentIndex.value = 4
		}
	}
	

5.设置切换导航项;

       <view class="nav_box" id="nav_box" :style="{top:nav_height + 'px'}">
			<view class="nav_item" :class="{'active':index == currentIndex}" v-for="item,index in navList" :key="index" @click="navHandle(index)">{{item.title}} </view>
		</view>
// 切换导航位置
    const scroll_top = ref(0)// 滚动头部跳转的位置
	const navHandle = (e) => {
		switch (e) {
		   case 0:  
		     scroll_top.value = cpNode.value ? cpNode.value : 0; // 使用 offsetTop 获取节点顶部位置  
		     break;  
		   case 1:  
		     scroll_top.value = cbNode.value ? cbNode.value : 0;  
		     break;  
		   case 2: // 注意:这里你写的是 1,但我假设你可能是想写 3  
		    scroll_top.value = pjNode.value ? pjNode.value : 0; // 根据你的描述,这里似乎仍然是 pjNode  
		     break;  
		   case 3:  
		    scroll_top.value = yyNode.value ? yyNode.value : 0;  
		     break;  
		   default:  
		    scroll_top.value = sgNode.value ? sgNode.value : 0; // 默认滚动到 sgNode  
		 }  
		
	}

完整代码

<template>
	<view class="detail">
        <!-- 导航部分 -->
		<view class="nav_box" id="nav_box" :style="{top:nav_height + 'px'}">
			<view class="nav_item" :class="{'active':index == currentIndex}" v-for="item,index in navList" :key="index" @click="navHandle(index)">{{item.title}} </view>
		</view>
		<view class="fill" :style="{height:nav_box_height + 'px'}">
			
		</view>
		 <!-- 滑动内容部分 -->
		<scroll-view ref="scrollView" scroll-y="true" :scroll-top="scroll_top" class="scroll_box" :style="{height: window_height + 'px'}" @scroll="scrollHandle">
			<view id="cp"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} cp</view>
			<view id="cb"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} cb</view>
			
			<view id="pj" ></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} pj</view>
			
			<view id="yy"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} yy</view>
			
			<view id="sg"></view>
				<view class="item" v-for="item,index in 50" :key="index">{{item}} sg</view>
			
			
		</scroll-view>
		
	</view>
</template>

<script setup>
	import { ref,onMounted } from "vue"
	// 导航列表
	const navList = ref([
		{
			id:1,
			title:"产品",
		},
		{
			id:2,
			title:"成本",
		},
		{
			id:3,
			title:"评论",
		},
		{
			id:4,
			title:"运营",
		},
		{
			id:5,
			title:"事故"
		},
	])
	const currentIndex = ref(0)// 选中导航
	const scroll_top = ref(0)// 滚动头部跳转的位置
	// 各部分距离头部的高度
	const scrollView = ref(null)
	const cpNode = ref(null)
	const cbNode = ref(null)
	const pjNode = ref(null)
	const yyNode = ref(null)
	const sgNode = ref(null)
	
	// 视图可用高度
	const window_height = ref(0)
	const nav_height = ref(0)//头部导航条高度
	const nav_box_height = ref(0)//导航高度
	// 切换导航位置
	const navHandle = (e) => {
		switch (e) {
		   case 0:  
		     scroll_top.value = cpNode.value ? cpNode.value : 0; // 使用 offsetTop 获取节点顶部位置  
		     break;  
		   case 1:  
		     scroll_top.value = cbNode.value ? cbNode.value : 0;  
		     break;  
		   case 2: // 注意:这里你写的是 1,但我假设你可能是想写 3  
		    scroll_top.value = pjNode.value ? pjNode.value : 0; // 根据你的描述,这里似乎仍然是 pjNode  
		     break;  
		   case 3:  
		    scroll_top.value = yyNode.value ? yyNode.value : 0;  
		     break;  
		   default:  
		    scroll_top.value = sgNode.value ? sgNode.value : 0; // 默认滚动到 sgNode  
		 }  
		
	}
    const scrollHandle = (e) => {
		let scrollTop = e.detail.scrollTop
		if(scrollTop>=cpNode.value && scrollTop<cbNode.value){
			currentIndex.value = 0
		}else if(scrollTop>=cbNode.value && scrollTop<pjNode.value){
			currentIndex.value = 1
		}else if(scrollTop>=pjNode.value && scrollTop<yyNode.value){
			currentIndex.value = 2
		}else if(scrollTop>=yyNode.value && scrollTop<sgNode.value){
			currentIndex.value = 3
		}else{
			currentIndex.value = 4
		}
	}
	onMounted(()=>{
		// 设置导航头部高度、导航高度
		let  SYSTEM_INFO =  uni.getSystemInfoSync()
		const nav_box = document.getElementById("nav_box").offsetHeight
		nav_box_height.value = nav_box
		window_height.value = SYSTEM_INFO.windowHeight - nav_box
		nav_height.value = SYSTEM_INFO.windowTop
		
		// 获取滑动各个子节点的滑动高度
		const query = uni.createSelectorQuery().in(scrollView.value)
		query.select('#cp').boundingClientRect((rect)=>{
			cpNode.value = rect.top
		}).exec();
		query.select('#cb').boundingClientRect((rect)=>{
			cbNode.value = rect.top
		}).exec();
		query.select('#pj').boundingClientRect((rect)=>{
			pjNode.value = rect.top
		}).exec();
		query.select('#yy').boundingClientRect((rect)=>{
			yyNode.value = rect.top
		}).exec();
		query.select('#sg').boundingClientRect((rect)=>{
			sgNode.value = rect.top
		}).exec();
	})
	
</script>

<style lang="scss" scoped>
	.detail{
		.top{
			display: flex;
			align-items: center;
			padding: 0 30rpx;
			height: 100rpx;
			.search{
				flex: 1;
			}
			.top_icon{
				width: 60rpx;
				height: 60rpx;
				padding: 20rpx;
				margin-left: 20rpx;
				image{
					width: 100%;
					height: 100%;
				}
			}
			.share{
				width: 50rpx;
				height: 50rpx;
			}
		}
		.nav_box{
			position: fixed;
			left: 0;
			right: 0;
			display: flex;
			justify-content: space-around;
			align-items: center;
			height: 100rpx;
			padding: 0 30rpx;
			box-shadow: 5rpx 5rpx 10rpx rgba(0, 0,0, 0.5);
			background-color: #fff;
			z-index: 10;
			.nav_item{
				padding: 20rpx;
				// background-color: pink;
				
			}
			.active{
				color: $theme-color;
			}
		}
		.scroll_box{
			background-color: pink;
		}
	}

</style>

扩展:步骤五可以通过中的scroll-into-view属性进行切换(当不需要进行滑动框内容对导航绑定时可以进行使用,例如:常规分类页中左右结构的滑动,*代码量会减少一些)

       <view class="nav_box" id="nav_box" :style="{top:nav_height + 'px'}">
			<view class="nav_item" :class="{'active':index == currentIndex}" v-for="item,index in navList" :key="index" @click="navHandle(index)">{{item.title}} </view>
		</view>
		<view class="fill" :style="{height:nav_box_height + 'px'}">
			
		</view>
		<scroll-view scroll-y="true" :scroll-into-view="activeId" :style="{height: window_height + 'px'}">
			<view id="cp"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} cp</view>
			<view id="cb"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} cb</view>
			
			<view id="pj" ></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} pj</view>
			
			<view id="yy"></view>
				<view class="item" v-for="item,index in 20" :key="index">{{item}} yy</view>
			
			<view id="sg"></view>
				<view class="item" v-for="item,index in 50" :key="index">{{item}} sg</view>
			
			
		</scroll-view>
<script setup>
	import { ref,onMounted } from "vue"
	// 导航列表
	const navList = ref([
		{
			id:1,
			title:"产品",
		},
		{
			id:2,
			title:"成本",
		},
		{
			id:3,
			title:"评论",
		},
		{
			id:4,
			title:"运营",
		},
		{
			id:5,
			title:"事故"
		},
	])
	const currentIndex = ref(0)// 选中导航
	// 各部分距离头部的高度
	
	// 视图可用高度
	const window_height = ref(0)
	const nav_height = ref(0)//头部导航条高度
	const nav_box_height = ref(0)//导航条高度// 导航高度
	
	const activeId = ref('')
	 
	// 切换导航位置
	const navHandle = (e) => {
		currentIndex.value  = e
		switch (e) {
		case 0:
		  activeId.value = 'cp' 
		  break;  
		case 1:  
		  activeId.value = 'cb'
		  break;  
		case 2:  
		activeId.value = 'pj'
		  break;  
		case 3:  
		activeId.value = 'yy'
		  break;  
		default:  
		activeId.value = 'sg'
		}
		
	}
	onMounted(()=>{
		// 设置导航头部高度、导航高度
		let  SYSTEM_INFO =  uni.getSystemInfoSync()
		const nav_box = document.getElementById("nav_box").offsetHeight
		nav_box_height.value = nav_box
		window_height.value = SYSTEM_INFO.windowHeight - nav_box
		nav_height.value = SYSTEM_INFO.windowTop
		
		
	})
	
</script>

*存在优化法:将节点名称放入一个数组中,例:

const scrollIds = ['cp', 'cb', 'pj', 'yy', 'sg'];
const navHandle = (e) => {
		currentIndex.value  = e
		activeId.value = scrollIds[e]	
	}
;