vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue3点击Tab平滑滚动并吸顶

基于Vue3实现点击Tab平滑滚动并吸顶效果的完整代码

作者:Mr Xu_

在现代 Web 应用开发中,良好的用户体验至关重要,本文将深入剖析一个常见的交互需求:点击页面 Tab 栏时,页面平滑滚动至该 Tab 区域,并使其吸附在顶部导航栏下方的效果,需要的朋友可以参考下

前言

在现代 Web 应用开发中,良好的用户体验至关重要。本文将深入剖析一个常见的交互需求:点击页面 Tab 栏时,页面平滑滚动至该 Tab 区域,并使其吸附在顶部导航栏下方。我们将结合真实项目代码,从实现原理、关键逻辑到最佳实践进行详细讲解。

一、需求背景

在页面中,存在多个 Tab(如“公司简介”、“资质荣誉”等)。用户点击任意 Tab 时,期望:

  1. 页面平滑滚动,使 Tab 栏刚好位于顶部导航(Header)下方;
  2. 滚动完成后,Tab 栏固定吸附在 Header 下方,随页面滚动保持位置不变;
  3. 同时更新路由和子组件内容。

注意:本项目使用 Vue3 + <script setup> + Less,滚动容器为自定义 .layout(非 window),且仅针对 PC 端。

二、核心实现思路

要实现上述效果,需解决两个关键问题:

1.如何让元素“吸顶”?

使用 CSS position: sticky

2.如何平滑滚动到目标位置?

计算目标元素相对于滚动容器的偏移量
调用 scrollTo({ top: target, behavior: 'smooth' })

三、代码分析(以index.vue为例)

1. 模板结构(关键部分)

<template>
  <div class="container">
    <!-- 背景图 -->
    <div class="bgpic">...</div>
    
    <!-- Tab 栏(添加 ref 用于获取 DOM) -->
    <div class="tab" :class="{'h5-tab': isMobile}" ref="tabRef">
      <div class="tab-box">
        <div class="tab-content">
          <span 
            v-for="item in sortedMenuList" 
            :key="item.id"
            @click="handleChose(item)"
            :class="{ 'current-tab': currentTab == item.menuName }"
          >
            {{ item.menuName }}
          </span>
        </div>
      </div>
    </div>

    <!-- 内容区(各 Tab 对应组件) -->
    <div class="content">
      <div v-show="currentTab == '流域项目'">...</div>
      <div v-show="currentTab == '省级项目'">...</div>
      ...
    </div>
  </div>
</template>

关键点:给 .tab 添加 ref="tabRef",便于 JS 获取其 DOM 位置。

2. CSS 吸顶样式(Less)

.tab {
  height: 96px;
  background-color: #eee;
  display: flex;
  justify-content: center;
  align-items: center;
  
  /* 👇 核心:sticky 定位实现吸顶 */
  position: sticky;
  top: 95px; /* 与 App.vue 中 .content 的 padding-top 一致 */
  z-index: 10;
}

为什么是 95px?
查看 App.vue 可知:

.content { padding-top: v-bind('isWelcome ? 0 : "95px"'); }

非首页时,.content 上边距为 95px,即 Header 高度。因此 Tab 吸顶位置为 top: 95px。

3. JavaScript 逻辑:handleChose方法

const handleChose = (val, skipAnimation = false) => {
  // 1. 更新当前 Tab 和菜单 ID
  currentTab.value = val.menuName;
  menuId.value = val.id;

  // 2. 重置子组件状态(略)

  // 3. 【关键】移动端或跳过动画时直接返回
  if (isMobile.value || skipAnimation) return;

  // 4. 平滑滚动到 Tab 位置
  nextTick(() => {
    const bgpic = document.querySelector('.bgpic');
    const bgpicHeight = bgpic ? bgpic.offsetHeight : 0;
    const layout = document.querySelector('.layout');
    
    if (layout) {
      setTimeout(() => {
        // 目标滚动位置 = 背景图高度(即 Tab 的 offsetTop)
        layout.scrollTo({
          top: bgpicHeight,
          behavior: 'smooth'
        });
      }, 0);
    }
  });
};

代码解析:

bgpicHeight 即 Tab 的 offsetTop
因为 .tab 紧跟在 .bgpic 后,且 .bgpic 高度固定为 550px,所以 bgpic.offsetHeight 就是 Tab 距离容器顶部的距离。

为什么用 setTimeout
确保 DOM 更新完成后再执行滚动(虽然 nextTick 已保证,但双重保险)。

为什么不直接计算 tabRef.value.offsetTop
在复杂布局中,offsetTop 可能受父元素定位影响。而本项目结构简单(.bgpic.tab),直接用背景图高度更可靠。

注意:此写法依赖固定布局。若 Tab 位置动态变化,应改用通用方法计算 offsetTop(见下文优化建议)。

四、优化建议(通用方案)

如果 Tab 位置不固定,可采用以下通用方法计算偏移量:

// 通用获取元素相对于滚动容器的 offsetTop
function getOffsetTop(element, container) {
  let offsetTop = 0;
  let el = element;
  while (el && el !== container && el !== document.body) {
    offsetTop += el.offsetTop;
    el = el.offsetParent;
  }
  return offsetTop;
}

// 在 handleChose 中使用
nextTick(() => {
  const layout = document.querySelector('.layout');
  const tabEl = tabRef.value;
  if (layout && tabEl) {
    const offsetTop = getOffsetTop(tabEl, layout);
    const headerHeight = 95; // 或动态获取
    layout.scrollTo({
      top: offsetTop - headerHeight,
      behavior: 'smooth'
    });
  }
});

五、总结

技术点实现方式说明
吸顶效果position: sticky; top: 95px利用 CSS 原生能力,性能好
平滑滚动element.scrollTo({ behavior: 'smooth' })原生 API,无需第三方库
位置计算利用背景图高度 / 通用 offsetTop 计算根据项目结构选择
响应式处理移动端跳过滚动逻辑提升移动端体验

通过以上方案,我们实现了点击 Tab → 平滑滚动 → 吸顶固定的完整交互,既满足了产品需求,又保证了代码的可维护性。

最佳实践:优先使用 CSS sticky 实现吸顶,JS 仅负责触发滚动;避免用 JS 动态修改 position,减少重绘开销。

以上就是基于Vue3实现点击Tab平滑滚动并吸顶效果的完整代码的详细内容,更多关于Vue3点击Tab平滑滚动并吸顶的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文