实现侧边栏点击标题列表,和中间列表区域联动效果

左侧边栏标题列表实现:

-------------------html-----------------------
 <div
      class="uav"
      :class="{ hidden: !isVisible, visible: isVisible }">
      <ul id="toc">
        <li
          v-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 = true

  const 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} 的元素`);
  }
  // 更新选中的区块ID
  activeSectionId.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.pageYOffset
  if (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]">
          <div
            v-show="item.box.length"
            v-for="(item, index) in homeBoxList"
            :key="item.id">
                        <div
                          v-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>
...
        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值