Vue 3 锚点组件实现
在这篇文章中,我们将介绍如何使用 Vue 3 和一些现代 Web 技术实现一个锚点导航组件。该组件用于在页面中展示文章目录,用户可以点击目录项快速跳转到页面中的相应部分,并且在用户滚动页面时,高亮显示当前所在的章节。
组件结构
本锚点组件包含两个主要部分:
- 侧边栏:用于显示文章的目录列表。每个目录项都可以是一个独立的链接,或者包含子项。
- 滚动监听:在用户滚动页面时,根据页面滚动位置动态更新高亮显示的目录项。
下面将详细讲解组件的实现。
组件模板
<template>
<view class="container">
<section ref="fixedContainer" class="blog-side-anchor fixed" :style="{ top: topDistance }">
<div>
<ul class="anchor">
<div v-if="items.length" class="line" :style="{ height: lineHeight + 'px' }">
<div
v-show="moveLineTop !== 0"
class="move-line"
:style="{ top: moveLineTop + 'px' }"
></div>
</div>
<div>
<li v-for="(v, i) in items" :key="i" class="li">
<a
class="anchor-link first-level"
:class="{ active: activeLink === v.href }"
:href="'#' + v.href"
@click="onAnchorClick(v)"
>
<span>{{ v.title }}</span>
</a>
<!-- 渲染子项 -->
<ul v-if="v.children?.length" class="collapse li">
<li v-for="(n, k) in v.children" :key="k">
<a
class="anchor-link"
:class="{ active: activeLink === n.href }"
:href="'#' + n.href"
style="width: 224px"
@click="onAnchorClick(v)"
>
{{ n.title }}
</a>
</li>
</ul>
</li>
</div>
</ul>
<div class="custom"></div>
</div>
</section>
</view>
</template>
关键部分:
-
锚点列表 (
<ul class="anchor">
):- 该部分渲染了文章目录,通过
v-for
循环遍历items
数组。 - 每个目录项 (
<a>
) 都是一个链接,点击时触发onAnchorClick
方法。
- 该部分渲染了文章目录,通过
-
滚动条和移动线 (
<div class="line">
):- 在目录列表上方有一条纵向线条,表示滚动位置。这个移动线会根据页面滚动更新位置,以指示当前所在的章节。
组件逻辑
组件的核心逻辑实现了以下几个功能:
1. 监听滚动事件
组件通过监听容器的滚动事件来动态更新当前的活动目录项。
const handleScroll = () => {
flag.value = true;
const currentPosition = scrollContainer.value ? scrollContainer.value.scrollTop : 0;
for (let i = offsetList.value.length - 1; i >= 0; i--) {
const section = offsetList.value[i];
if (currentPosition >= section.offsetTop!) {
activeLink.value = section.href;
updateMoveLinePosition(section.href);
break;
}
}
lastPosition.value = currentPosition;
flag.value = false;
};
- 每次滚动时,
handleScroll
会检查当前滚动位置currentPosition
,然后遍历offsetList
获取每个锚点项的位置,更新activeLink
和移动线的位置。
2. 获取锚点列表及其位置
const getOffsetList = () => {
let list: AnchorItemProps[] = [];
props.items.forEach(item => {
if (item.children) {
list.push(...item.children);
}
list.push(item);
});
// 重新计算每个目录项的 offsetTop
offsetList.value = list
.map(item => {
let targetElement = document.getElementById(item.href) as HTMLElement;
let top = Math.round(targetElement?.offsetTop) - 10;
return {
title: item.title,
href: item.href,
offsetTop: top,
};
})
.sort((a, b) => a.offsetTop - b.offsetTop); // 按照 offsetTop 排序
};
getOffsetList
方法计算每个目录项对应的页面位置(offsetTop
),并将其保存在offsetList
数组中。此数组将用于滚动时匹配当前的锚点。
3. 更新移动线位置
const updateMoveLinePosition = (href: string) => {
let aList = document.querySelectorAll('.anchor .anchor-link');
for (let i = 0; i < aList.length; i++) {
if (aList[i].getAttribute('href') === `#${href}`) {
moveLineTop.value = aList[i].offsetTop;
break;
}
}
};
updateMoveLinePosition
根据当前活动的href
更新移动线的位置,确保用户能够看到当前所在的章节。
样式
在样式部分,我们使用了 less
来处理页面布局和样式。主要的样式包括:
- 侧边栏布局:固定定位,右侧显示,宽度 240px,最大高度 600px。
- 目录列表:通过
flex
布局将目录项垂直排列,并隐藏滚动条。 - 高亮显示:当前活动的目录项会使用
.active
类进行高亮。
.fixed {
position: fixed;
top: 150px;
right: 0;
z-index: 1000;
width: 240px;
display: flex;
justify-content: flex-start; /* 垂直排列内容 */
}
.anchor {
border-radius: 4px;
padding: 21px 24px;
width: 204px;
max-height: 600px;
overflow-y: scroll;
position: relative; /* 添加相对定位 */
display: flex;
flex-direction: column; /* 使子项竖着排列 */
}
.line {
position: absolute;
top: 0;
left: 10px;
width: 2px;
height: calc(100% - 21px);
background-color: #ededed;
z-index: 99;
.move-line {
position: absolute;
width: 2px;
height: 16px;
background-color: #7367f0;
z-index: 100;
transition: top 0.3s ease; /* 添加过渡动画 */
}
}
.active {
color: #7367f0 !important;
}
.anchor::-webkit-scrollbar {
display: none;
}
适配响应式
为了适配不同设备的屏幕尺寸,当屏幕宽度小于 1024px 时,隐藏锚点导航栏:
@media (max-width: 1024px) {
.anchor {
display: none;
}
}
总结
这个 Vue 3 锚点导航组件提供了一种便捷的方式来实现页面内的快速导航。它能够在页面滚动时动态高亮显示当前所在章节,并且支持嵌套的子目录项。组件的核心包括:
- 监听滚动事件并根据滚动位置更新锚点高亮。
- 使用
offsetTop
获取锚点的相对位置。 - 使用 CSS 和 Vue 动态更新样式,确保用户体验。
通过这个组件,用户可以在长篇文章中轻松地跳转到指定章节,提高了页面的可用性和导航体验。