项目中自己写的一个小功能,先看看效果
上面的菜单部分滑动时会自动置顶,点击部分后会定位到页面指定位置,且标题随着滚动的页面内容实时高亮。这个功能大部分官网都有,一说就懂。下面说一下实现过程。
实现过程
1.标题栏的定位
标题栏位置按照正常文档流写入,使用sticky定位,top设置为0就可以实现。过去在sticky出现之前,一般会使用动态class设置粘性布局,但是这太不优雅了,还需要js逻辑辅助。但是sticky的出现完美解决了这一问题。代码如下
html
其中scrollToView是绑定的点击跳转事件,menuIndex用于高亮
<div ref="menuBox" class="menu-box">
<ul class="menu-box__list">
<li
v-for="(item, index) in menuList"
:key="index"
:class="menuIndex === index ? 'check' : ''"
@click="scrollToView(index)"
>
{{ item.name }}
</li>
</ul>
</div>
css
.menu-box {
position: sticky;
top: 0;
z-index: 999;
width: 100%;
margin-top: -40px;
color: #fff;
background: linear-gradient(90deg, #1e2e45 0%, rgba(12, 60, 109, 0.5) 50%, #162131 100%);
ul {
display: flex;
justify-content: space-between;
width: 1200px;
min-width: 1200px;
height: 40px;
margin: 0 auto;
}
li {
display: inline-block;
line-height: 40px;
cursor: pointer;
}
.check {
color: #a4ddff;
position: relative;
}
.check::after {
position: absolute;
content: '';
bottom: 2px;
height: 1px;
width: 40px;
right: 50%;
transform: translateX(50%);
background: #a4ddff;
}
}
2.点击滚动到指定位置切高亮
点击定位实现是通过滚动监听,设置ref动态读取位置实现的。代码如下:
html
html部分在上面的基础上,增加底下的页面内容元素,这里的css就不放了
<div ref="jjfa" class="content-box jjfa">
<div class="content-box__title">{{ menuList[0].name }}</div>
<div class="back"></div>
</div>
<div ref="jcfw" class="content-box jcfw">
<div class="content-box__title">{{ menuList[1].name }}</div>
<div class="back"></div>
</div>
<div ref="bafw" class="content-box bafw">
<div class="content-box__title">{{ menuList[2].name }}</div>
<div class="back"></div>
</div>
<div ref="sffw" class="content-box sffw">
<div class="content-box__title">{{ menuList[3].name }}</div>
<div class="back"></div>
</div>
<div ref="hzfw" class="content-box hzfw">
<div class="content-box__title">{{ menuList[4].name }}</div>
<div class="back"></div>
</div>
<div ref="ldqk" class="content-box ldqk">
<div class="content-box__title">{{ menuList[5].name }}</div>
<div class="back"></div>
</div>
<div ref="dxal" class="content-box dxal">
<div class="content-box__title">{{ menuList[6].name }}</div>
<div class="back"></div>
</div>
实现逻辑
以下内容分部分介绍
1.data部分,menuList用来存放页面ref值以及各计算出来的ref部分到首和尾到页面顶部的高度(高度在加载时赋值),menuIndex用来动态给标题高亮
data() {
return {
menuList: [
{ name: '标题1', topToTop: 0, botToTop: 0, ref: 'jjfa' },
{ name: '标题2', topToTop: 0, botToTop: 0, ref: 'jcfw' },
{ name: '标题3', topToTop: 0, botToTop: 0, ref: 'bafw' },
{ name: '标题4', topToTop: 0, botToTop: 0, ref: 'sffw' },
{ name: '标题5', topToTop: 0, botToTop: 0, ref: 'hzfw' },
{ name: '标题6', topToTop: 0, botToTop: 0, ref: 'ldqk' },
{ name: '标题7', topToTop: 0, botToTop: 0, ref: 'dxal' }
],
menuIndex: 0
}
},
2.设置监听,页面加载addEventListener滚动事件,滚动监听事件用来高亮对应标题使用
mounted() {
window.addEventListener('scroll', this.handleScroll)
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll)
},
methods: {
handleScroll() {
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
this.menuList.forEach((item, index) => {
if (scrollTop >= item.topToTop && scrollTop < item.botToTop) {
this.menuIndex = index
return
}
})
},
}
3.页面加载时获取各元素到top的高度
mounted() {
this.initContentBoxDistance()
},
methods: {
initContentBoxDistance() {
this.menuList.forEach((item, index) => {
this.menuList[index].topToTop = getElementViewTop(this.$refs[item.ref])
this.menuList[index].botToTop = this.$refs[item.ref].clientHeight + this.menuList[index].topToTop
})
},
getElementViewTop(element){
let actualTop = element.offsetTop
let current = element.offsetParent
while (current !== null) {
actualTop += current.offsetTop
current = current.offsetParent
}
return actualTop
}
}
4.为按钮添加点击滚动事件
scrollToView(index) {
document.getElementsByClassName('content-box')[index].scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'nearest'
})
},
5.完整代码
export default {
name: 'Home',
components: {
TitleBanner
},
data() {
return {
menuList: [
{ name: '标题1', topToTop: 0, botToTop: 0, ref: 'jjfa' },
{ name: '标题2', topToTop: 0, botToTop: 0, ref: 'jcfw' },
{ name: '标题3', topToTop: 0, botToTop: 0, ref: 'bafw' },
{ name: '标题4', topToTop: 0, botToTop: 0, ref: 'sffw' },
{ name: '标题5', topToTop: 0, botToTop: 0, ref: 'hzfw' },
{ name: '标题6', topToTop: 0, botToTop: 0, ref: 'ldqk' },
{ name: '标题7', topToTop: 0, botToTop: 0, ref: 'dxal' }
],
menuIndex: 0
}
},
mounted() {
window.addEventListener('scroll', this.handleScroll)
this.initContentBoxDistance()
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll)
},
methods: {
scrollToView(index) {
document.getElementsByClassName('content-box')[index].scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'nearest'
})
},
handleScroll() {
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
this.menuList.forEach((item, index) => {
if (scrollTop >= item.topToTop && scrollTop < item.botToTop) {
this.menuIndex = index
return
}
})
},
initContentBoxDistance() {
this.menuList.forEach((item, index) => {
this.menuList[index].topToTop = getElementViewTop(this.$refs[item.ref])
this.menuList[index].botToTop = this.$refs[item.ref].clientHeight + this.menuList[index].topToTop
})
},
getElementViewTop(element){
let actualTop = element.offsetTop
let current = element.offsetParent
while (current !== null) {
actualTop += current.offsetTop
current = current.offsetParent
}
return actualTop
}
}
}
结尾
原创内容,多多批评都铎优化建议多多指正。一起讨论,thanks