导航栏吸顶效果 与内容联动组件
<template>
<div>
<div class="nav-wrapper" :class="{ fixed: fixed }">
<div class="nav">
<div
class="nav-item"
v-for="(section, index) in sections"
:key="index"
@click="scrollToSection(section, index)"
:class="{ active: activeIndex == index }"
>
{{ section.title }}
</div>
</div>
</div>
<div class="content">
<div
ref="content"
class="section"
v-for="(section, index) in sections"
:key="index"
:id="section.id"
>
<img :src="section.src" />
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
fixed: false,
activeIndex: 0,
contentTopList: []
};
},
props: {
sections: {
type: Object,
default: {},
},
},
mounted() {
// this.sections = [
// {
// id: "section1",
// src: "https://static-core.meditrusthealth.com/b4115419bdb64c2e835c241e726b799c.png",
// title: "套装详情",
// },
// {
// id: "section2",
// src: "https://static-core.meditrusthealth.com/8ffb2d0c4f094c3db0b006d9d6913998.png",
// title: "使用流程",
// },
// {
// id: "section3",
// src: "https://static-core.meditrusthealth.com/9d18ea0e43094ffe9f80790d8629c79c.png",
// title: "体检须知",
// },
// ];
window.addEventListener("scroll", this.handleScroll);
},
beforeDestroy() {
window.removeEventListener("scroll", this.handleScroll);
},
methods: {
getContentTopList() {
this.contentTopList = this.$refs.content.map(item => item.offsetTop)
},
handleScroll() {
this.fixed = window.pageYOffset > 480;
this.contentTopList = this.$refs.content.map(item => item.offsetTop)
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
for (let i = this.contentTopList.length - 1; i >= 0; i--) {
if (scrollTop >= this.contentTopList[i] - 190) {
this.activeIndex = i
break
}
}
},
scrollToSection(section, index) {
this.activeIndex = index;
const el = document.getElementById(section.id);
const offsetTop = el.offsetTop - 70;
window.scrollTo({
top: offsetTop,
behavior: "smooth",
});
},
},
};
</script>
<style>
.nav-wrapper {
position: relative;
z-index: 1;
}
.nav-wrapper.fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
}
.nav {
display: flex;
justify-content: space-between;
padding: 5px 26px;
background: #fff;
height: 46px;
line-height: 40px;
font-size: 16px;
box-sizing: border-box;
}
.nav-item {
cursor: pointer;
font-size: 16px;
}
.content {
margin-top: 20px;
/* padding: 16px 8px;
background: #fff; */
margin: 12px 16px;
}
.section {
margin-bottom: 30px;
/* margin: 0 10px; */
}
.section p {
font-size: 16px;
}
.section img {
width: 100%;
height: auto;
}
h2 {
margin-top: 0;
}
p {
margin-bottom: 0;
}
.active {
color: #2945ff;
position: relative;
}
.active::after {
content: "";
display: block;
width: 28px;
height: 4px;
background: #2945ff;
position: absolute;
bottom: -4px;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
border-radius: 2px;
}
</style>
window.addEventListener("scroll", this.handleScroll) 来监听页面滚动事件,当页面滚动时,会触发 handleScroll 方法,根据页面滚动的位置来判断是否需要将导航栏固定在页面顶部。
在导航栏中,我们使用了 v-for 循环来渲染每个导航项,同时绑定了 @click 事件来实现点击导航项后滚动到对应的内容区块
在内容区块中,我们使用了 v-for 循环来渲染每个内容区块,同时给每个内容区块设置了一个 id,用来与导航栏中的链接关联起来
在 scrollToSection 方法中,我们使用 document.getElementById(section.id) 来获取对应的内容区块元素,然后使用 window.scrollTo 方法来实现页面滚动到对应的位置。这里使用了 behavior: "smooth" 来实现平滑滚动的效果
注意注意:不要在 mounted 里去获取元素位置,可能有图片未加载完,图片加载完后高度和元素位置发生了改变