Vue2实现导航栏绑定内容锚点+滚动动画

 html

<template>
	<div>
		<!-- 内容区域 -->
		<div class="content">
			<div>content-0</div>
			<div>content-1</div>
			<div>content-2</div>
			<div>content-3</div>
			<div>content-4</div>
		</div>
		<!-- 导航区域 -->
		<ul class="navs">
			<li :class="{ active: active === 0 }" @click="scrollTo(0)">content-0</li>
			<li :class="{ active: active === 1 }" @click="scrollTo(1)">content-1</li>
			<li :class="{ active: active === 2 }" @click="scrollTo(2)">content-2</li>
			<li :class="{ active: active === 3 }" @click="scrollTo(3)">content-3</li>
			<li :class="{ active: active === 4 }" @click="scrollTo(4)">content-4</li>
		</ul>
	</div>
</template>

css

<style scoped>
	/* 内容区的样式 */
	.content {
		background-color: white;
		width: 500px;
	}
	.content div {
		width: 100%;
		height: 600px;
		font-size: 36px;
		padding: 20px;
		background-color: #7ec384;
	}
	.content div:nth-child(2n) {
		background-color: #847ec3;
	}
	/* 导航栏的样式 */
	.navs {
		position: fixed;
		top: 80px;
		left: 700px;
		background-color: #efefef;
	}
	.navs li {
		padding: 0 20px;
		line-height: 1.6;
		font-size: 24px;
		list-style: none;
		cursor: pointer;
	}
	/* 当导航被点亮后改变颜色 */
	.navs .active {
		color: #847ec3;
		background-color: #e2e2e2;
	}
</style>

js

<script>
	export default {
		props: {},
		data() {
			return {
				active: 0, // 当前激活的导航索引
			}
		},
		mounted() {
			// 监听滚动事件
			window.addEventListener('scroll', this.onScroll, false)
		},
		destroy() {
			// 必须移除监听器,不然当该vue组件被销毁了,监听器还在就会出错
			window.removeEventListener('scroll', this.onScroll)
		},
		methods: {
			// 滚动监听器
			onScroll() {
				// 获取所有锚点元素
				const navContents = document.querySelectorAll('.content div')
				// 所有锚点元素的 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
					}
					// console.log(scrollTop)  滚动的距离
					// console.log(document.documentElement.clientHeight) 屏幕高度
					// console.log(document.documentElement.scrollHeight) body的高度
					if (scrollTop + document.documentElement.clientHeight === document.documentElement.scrollHeight) {
						navIndex = offsetTopArr.length - 1
					}
				}
				this.active = navIndex
			},
			// 跳转到指定索引的元素
			scrollTo(index) {
				// 获取目标的 offsetTop
				// css选择器是从 1 开始计数,我们是从 0 开始,所以要 +1
				const targetOffsetTop = document.querySelector(`.content div:nth-child(${index + 1})`).offsetTop
				// 获取当前 offsetTop
				let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
				// 定义一次跳 50 个像素,数字越大跳得越快,但是会有掉帧得感觉,步子迈大了会扯到蛋
				const STEP = 100
				// 判断是往下滑还是往上滑
				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)
					}              
				}
			},
		},
	}
</script>

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可以通过以下几个步骤实现: 1. 在菜单中添加点击事件,获取对应的点id。 2. 使用`ref`属性获取到内容容器的元素。 3. 在内容容器上`scroll`事件,监听滚动位置。 4. 根据当前滚动位置,判断哪个点处于可视范围内,并高亮对应的菜单项。 具体代码如下: 菜单部分: ``` <template> <div> <ul> <li v-for="item in menu" :key="item.id" @click="scrollToAnchor(item.id)" :class="{active: activeAnchor === item.id}"> {{ item.title }} </li> </ul> </div> </template> <script> export default { data() { return { menu: [ { id: 'a1', title: '点1' }, { id: 'a2', title: '点2' }, { id: 'a3', title: '点3' } ], activeAnchor: '' // 当前激活的点id } }, methods: { scrollToAnchor(anchorId) { // 获取对应的点元素 const anchorElement = document.getElementById(anchorId); if (anchorElement) { // 滚动点位置 anchorElement.scrollIntoView({ behavior: 'smooth' }); // 设置当前激活的点id this.activeAnchor = anchorId; } } } } </script> ``` 内容部分: ``` <template> <div ref="content" @scroll="onScroll"> <div id="a1">点1内容</div> <div id="a2">点2内容</div> <<div id="a3">点3内容</div> </div> </template> <script> export default { methods: { onScroll() { // 获取内容容器的高度和滚动位置 const contentHeight = this.$refs.content.offsetHeight; const scrollTop = this.$refs.content.scrollTop; // 遍历所有点,判断哪个处于可视范围内 for (let i = 0; i < this.menu.length; i++) { const anchorId = this.menu[i].id; const anchorElement = document.getElementById(anchorId); if (anchorElement) { const anchorTop = anchorElement.offsetTop; const anchorBottom = anchorTop + anchorElement.offsetHeight; if (scrollTop >= anchorTop && scrollTop < anchorBottom) { // 设置当前激活的点id this.$emit('active-anchor', anchorId); break; } } } } } } </script> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值