這里監聽window 的
scroll
實現一個頁面滾動,導航菜單定位,內容聯動的一個簡單組件,結合一些案例,按需進行了整合,在此記錄一下
效果圖如下
![]() |
![]() |
具體實現如下
一、先創建一個NavigateTool.vue
導航組件
html
<template>
<div class="searchBar navigate__bar">
<ul :class="searchBarFixed == true ? 'isFixed' :''">
<li :class="{active: navIndex===0}" @click="scrollTo(0)">區域</li>
<li :class="{active: navIndex===1}" @click="scrollTo(1)">國界</li>
<li :class="{active: navIndex===2}" @click="scrollTo(2)">宇宙</li>
<li :class="{active: navIndex===3}" @click="scrollTo(3)">銀河</li>
</ul>
</div>
</template>
js
1、mounted()中 監聽頁面滾動
mounted() {
// 監聽滾動事件
window.addEventListener('scroll', this.onScroll, false)
},
2、 destroy() 中注銷監聽
destroy() {
// 必須移除監聽器,不然當該vue組件被銷毀了,監聽器還在就會出錯
window.removeEventListener('scroll', this.onScroll)
},
3、data掛載當前激活導航id
data(){
return{
navIndex:0,
searchBarFixed:false,
}
},
4、methods中添加 監聽頁面滾動事件方法
/** * 頁面滾動事件監聽 */
onScroll() {
// 獲取所有錨點元素
const navContents = document.querySelectorAll('.home__content_comp div')
console.log(navContents.length)
// 所有錨點元素的 offsetTop
const offsetTopArr = []
navContents.forEach(item => {
offsetTopArr.push(item.offsetTop)
})
// 獲取當前文檔流的 scrollTop
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
// 定義當前點亮的導航下標
let navIndex = 0
for (let n = 0; n < offsetTopArr.length; n++) {
// 如果 scrollTop 大於等於第n個元素的 offsetTop 則說明 n-1 的內容已經完全不可見
// 那么此時導航索引就應該是n了
if (scrollTop >=offsetTopArr[n]) {
navIndex = n+1
}
}
navIndex = navIndex >= navContents.length? navIndex-1:navIndex;
console.log(navIndex)
this.navIndex = navIndex
// 組件吸附頂部
var offsetTop = document.querySelector('.navigate__bar').offsetTop;
if (scrollTop > offsetTop) {
this.searchBarFixed = true;
} else {
this.searchBarFixed = false;
}
},
5、methods中添加導航菜單點擊事件(計算頁面滾動位置與組件距離頂部距離)
/** * 跳轉到指定索引的元素 * @param index */
scrollTo(index) {
// 獲取目標的 offsetTop
// css選擇器是從 1 開始計數,我們是從 0 開始,所以要 +1
// const foundEl = document.querySelector(`.home__content_comp div:nth-child(${index + 1})`);
// const elClientHeight = foundEl.clientHeight;
const targetOffsetTop = document.querySelector(`.home__content_comp div:nth-child(${index+1})`).offsetTop-40
// 獲取當前 offsetTop
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
// 定義一次跳 50 個像素,數字越大跳得越快,但是會有掉幀得感覺,步子邁大了會扯到蛋
const STEP = 50
// 判斷是往下滑還是往上滑
if (scrollTop > targetOffsetTop) {
// 往上滑
smoothUp()
} else {
// 往下滑
smoothDown()
}
// 定義往下滑函數
function smoothDown() {
// 如果當前 scrollTop 小於 targetOffsetTop 說明視口還沒滑到指定位置
if (scrollTop < targetOffsetTop) {
// 如果和目標相差距離大於等於 STEP 就跳 STEP
// 否則直接跳到目標點,目標是為了防止跳過了。
if (targetOffsetTop - scrollTop >= STEP) {
scrollTop += STEP
} else {
scrollTop = targetOffsetTop
}
document.body.scrollTop = scrollTop
document.documentElement.scrollTop = scrollTop
// 關於 requestAnimationFrame 可以自己查一下,在這種場景下,相比 setInterval 性價比更高
requestAnimationFrame(smoothDown)
}
}
// 定義往上滑函數
function smoothUp() {
if (scrollTop > targetOffsetTop) {
if (scrollTop - targetOffsetTop >= STEP) {
scrollTop -= STEP
} else {
scrollTop = targetOffsetTop
}
document.body.scrollTop = scrollTop
document.documentElement.scrollTop = scrollTop
requestAnimationFrame(smoothUp)
}
}
}
css
<style scoped lang="scss">
.searchBar {
width: 100%;
.isFixed {
position: fixed;
width: 100%;
top: 0;
left: 0;
z-index: 999;
}
ul {
height: 40px;
line-height: 40px;
display: flex;
margin:0;
padding: 0;
li {
font-size: 0.8rem;
flex: 1;
display: flex;
justify-items: center;
justify-content: center;
align-items: center;
align-content: center;
position: relative;
background-color: #fff;
&.active {
color: #847ec3;
background-color: #e2e2e2;
&:after {
content: " ";
position: absolute;
height: 1px;
width: 30px;
bottom: 6px;
left:calc(50% - 15px);
border-top: 2px #847ec3 solid;
}
}
}
}
}
</style>
二、再建個HomeContent.vue
這個頁面就比較簡單了
<template>
<div>
<naigate-tool></naigate-tool>
<!-- 內容區域 -->
<div class="home__content_comp">
<div>
This is first page.
</div>
<div>
This is second page.
</div>
<div>
This is third page.
</div>
<div>
This is fourth page.
</div>
</div>
</div>
</template>
<script type="text/ecmascript-6">
import NaigateTool from './NavigateTool';
export default {
name:'HomeContent',
components:{
NaigateTool
}
}
</script>
<style scoped>
/* 內容區的樣式 */
.home__content_comp {
background-color: white;
width: 100%;
}
.home__content_comp div {
width: 100%;
height: 600px;
font-size: 26px;
background-color: #7ec384;
}
.home__content_comp div:nth-child(2n) {
background-color: #847ec3;
}
</style>
參考案例: