基于Vue3實現(xiàn)點擊Tab平滑滾動并吸頂效果的完整代碼
前言
在現(xiàn)代 Web 應用開發(fā)中,良好的用戶體驗至關(guān)重要。本文將深入剖析一個常見的交互需求:點擊頁面 Tab 欄時,頁面平滑滾動至該 Tab 區(qū)域,并使其吸附在頂部導航欄下方。我們將結(jié)合真實項目代碼,從實現(xiàn)原理、關(guān)鍵邏輯到最佳實踐進行詳細講解。
一、需求背景
在頁面中,存在多個 Tab(如“公司簡介”、“資質(zhì)榮譽”等)。用戶點擊任意 Tab 時,期望:
- 頁面平滑滾動,使 Tab 欄剛好位于頂部導航(Header)下方;
- 滾動完成后,Tab 欄固定吸附在 Header 下方,隨頁面滾動保持位置不變;
- 同時更新路由和子組件內(nèi)容。
注意:本項目使用 Vue3 + <script setup> + Less,滾動容器為自定義 .layout(非 window),且僅針對 PC 端。
二、核心實現(xiàn)思路
要實現(xiàn)上述效果,需解決兩個關(guān)鍵問題:
1.如何讓元素“吸頂”?
使用 CSS position: sticky
- 設置
top: 95px(與 Header 高度一致) - 元素在滾動到指定位置時自動固定
2.如何平滑滾動到目標位置?
計算目標元素相對于滾動容器的偏移量
調(diào)用 scrollTo({ top: target, behavior: 'smooth' })
三、代碼分析(以index.vue為例)
1. 模板結(jié)構(gòu)(關(guān)鍵部分)
<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>
<!-- 內(nèi)容區(qū)(各 Tab 對應組件) -->
<div class="content">
<div v-show="currentTab == '流域項目'">...</div>
<div v-show="currentTab == '省級項目'">...</div>
...
</div>
</div>
</template>
關(guān)鍵點:給 .tab 添加 ref="tabRef",便于 JS 獲取其 DOM 位置。
2. CSS 吸頂樣式(Less)
.tab {
height: 96px;
background-color: #eee;
display: flex;
justify-content: center;
align-items: center;
/* ?? 核心:sticky 定位實現(xiàn)吸頂 */
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. 重置子組件狀態(tài)(略)
// 3. 【關(guān)鍵】移動端或跳過動畫時直接返回
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 更新完成后再執(zhí)行滾動(雖然 nextTick 已保證,但雙重保險)。
為什么不直接計算 tabRef.value.offsetTop?
在復雜布局中,offsetTop 可能受父元素定位影響。而本項目結(jié)構(gòu)簡單(.bgpic → .tab),直接用背景圖高度更可靠。
注意:此寫法依賴固定布局。若 Tab 位置動態(tài)變化,應改用通用方法計算 offsetTop(見下文優(yōu)化建議)。
四、優(yōu)化建議(通用方案)
如果 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; // 或動態(tài)獲取
layout.scrollTo({
top: offsetTop - headerHeight,
behavior: 'smooth'
});
}
});
五、總結(jié)
| 技術(shù)點 | 實現(xiàn)方式 | 說明 |
|---|---|---|
| 吸頂效果 | position: sticky; top: 95px | 利用 CSS 原生能力,性能好 |
| 平滑滾動 | element.scrollTo({ behavior: 'smooth' }) | 原生 API,無需第三方庫 |
| 位置計算 | 利用背景圖高度 / 通用 offsetTop 計算 | 根據(jù)項目結(jié)構(gòu)選擇 |
| 響應式處理 | 移動端跳過滾動邏輯 | 提升移動端體驗 |
通過以上方案,我們實現(xiàn)了點擊 Tab → 平滑滾動 → 吸頂固定的完整交互,既滿足了產(chǎn)品需求,又保證了代碼的可維護性。
最佳實踐:優(yōu)先使用 CSS sticky 實現(xiàn)吸頂,JS 僅負責觸發(fā)滾動;避免用 JS 動態(tài)修改 position,減少重繪開銷。
以上就是基于Vue3實現(xiàn)點擊Tab平滑滾動并吸頂效果的完整代碼的詳細內(nèi)容,更多關(guān)于Vue3點擊Tab平滑滾動并吸頂?shù)馁Y料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue+thinkphp5.1+axios實現(xiàn)文件上傳
這篇文章主要為大家詳細介紹了Vue+thinkphp5.1+axios實現(xiàn)文件上傳,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-05-05
Vue.js 時間轉(zhuǎn)換代碼及時間戳轉(zhuǎn)時間字符串
這篇文章主要介紹了Vue.js 時間轉(zhuǎn)換代碼及時間戳轉(zhuǎn)時間字符串,需要的朋友可以參考下2018-10-10
在?Vite項目中使用插件?@rollup/plugin-inject?注入全局?jQuery的過程詳解
在一次項目腳手架升級的過程中,將之前基于?webpack?搭建的項目移植到?Vite?構(gòu)建,這篇文章主要介紹了在?Vite項目中,使用插件?@rollup/plugin-inject?注入全局?jQuery,需要的朋友可以參考下2022-12-12
vue 需求 data中的數(shù)據(jù)之間的調(diào)用操作
這篇文章主要介紹了vue 需求 data中的數(shù)據(jù)之間的調(diào)用操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08

