实现侧边栏点击标题列表,和中间列表区域联动效果
左侧边栏标题列表实现:
-------------------html-----------------------<divclass="uav":class="{ hidden: !isVisible, visible: isVisible }"><ul id="toc"><liv-for="(item, index) in HotList":key="index":class="{ active: activeSectionId == item.id }"class="point tocbox"><a @click.prevent="scrollToSection(item.id)"> {{ item.name }}</a></li></ul></div>
--------------------js(v3)--------------------//标题列表数据获取
const HotList = ref([]);
const ObtainHotBox = () => {GetHotBox().then((res) => {if (res.data && res.data.data) {// 过滤出 box 不为空的数据const filteredData = res.data.data.filter(item => item.box && item.box.length > 0);HotList.value = filteredData;}});
};//实现点击左侧标题 中间内容区域滚动到对应标题位置
const isManualScrolling = ref(false)
const scrollToSection = (sectionId) => {// 启用手动滚动标记isManualScrolling.value = trueconst element = document.getElementById(sectionId);if (!element) return;console.log(`滚动到 id 为 ${sectionId} 的元素`, element);if (element) {const elementTop = element.getBoundingClientRect().top + window.scrollY;window.scrollTo({top: elementTop - 89, //减去固定头部高度behavior: "smooth",});} else {console.log(`未找到 id 为 ${sectionId} 的元素`);}// 更新选中的区块IDactiveSectionId.value = sectionId;// 500ms后释放锁定(超过滚动动画时间)setTimeout(() => {isManualScrolling.value = false}, 800)
};//检测中间区域内容
const isVisible = ref(false);
const targetPosition = ref(400);
const activeSectionId = ref(null);
const updateActiveSection = () => {// 如果是手动触发的滚动,跳过自动检测if (isManualScrolling.value) return// 监听滚动事件// 使用 window.scrollY 替代已弃用的 window.pageYOffsetif (window.scrollY > targetPosition.value) {isVisible.value = true;} else {isVisible.value = false;}const sections = document.querySelectorAll(".Title");let closest = null; // 初始值设为上一次的值let minDistance = Infinity;console.log('sections')sections.forEach((section) => {const rect = section.getBoundingClientRect();// 检查区块是否在视窗内或上方(但不超过视窗高度)if (rect.top >= 0 && rect.top < window.innerHeight) {// 选择最接近视窗顶部的区块if (rect.top <= minDistance) {minDistance = rect.top;closest = section.id;}}});activeSectionId.value = closest;
// console.log('当前激活的区块 ID:', activeSectionId.value,closest);
};onMounted(async () => {window.addEventListener("scroll", updateActiveSection);
});
onUnmounted(() => {window.removeEventListener("scroll", updateActiveSection);
});--------------------css------------------
.uav {position: fixed;left: 0.4rem;top: 30%;z-index: 99;#toc {width: 1.82rem;font-weight: 400;text-align: center;font-size: 0.12rem;color: #9373c3;background: url("../../common/assets/images/maodianbg.png") no-repeat;background-size: 100% 100%;padding: 0.2rem 0;.tocbox {width: 1.36rem;text-align: center;margin: 0.22rem auto;}.active {/* 选中状态的样式 */color: #c7c7ff;background: url("../../common/assets/images/acmaodian.png") no-repeat;background-size: 100% 100%;transition: all 0.3s ease-out;will-change: transform, opacity;}/* 隐藏默认滚动条减少视觉干扰 */html {scrollbar-width: none; /* Firefox */-ms-overflow-style: none; /* IE */}::-webkit-scrollbar {display: none; /* Chrome/Safari */}}
}
中间区域内容联动:给列表项添加Title类名,以及绑定动态:id="item.id"
<div class="mb-[1.6rem]"><divv-show="item.box.length"v-for="(item, index) in homeBoxList":key="item.id"><divv-if="item.src"class="flex-c mb-[0.1rem] Title":id="item.id"><div class="w-[5.5rem] h-[0.69rem] flex-c"><img class="h-full" :src="item.src" alt="" srcset="" /></div></div>
...