vue3+ant Design vue锚点定位组件
·
一、 做出的效果图

二、用vue3和ant Design vue的锚点组件画出页面
<div class="main">
<!-- 锚点组件 -->
<a-anchor
:affix="true"
:targetOffset="10"
@click="handleClick"
@change="onChange"
:getContainer="getContainer"
:getCurrentAnchor="getCurrentAnchor"
style="float: right;"
>
<a-anchor-link
v-for="item in state.timeData"
:key="item.id"
class="navLink"
:class="{ 'active': activeLink == item.id }"
:href="`#${item.id}`"
:title="item.title"
/>
</a-anchor>
<!-- 页面内容 -->
<div class="content">
<div style="font-size:2rem;margin:10px 0;">账号与设置功能更新日志</div>
<div ref="containerRef">
<div class="container" v-for="(item, index) in state.timeData" :key="index" :id="item.id">
<h2>{{ item.title }}</h2>
<h2>{{ item.name }}</h2>
<h3>{{ item.content }}</h3>
</div>
</div>
</div>
</div>
三、用ts写监听以及定位逻辑
1. 定义数据
const state = reactive({
timeData: [
{
id: 0,
title: '2024.11.1更新',
name: '安卓设备支持系统通行密钥',
content: '使用安卓设备设置通行密钥时,支持设置系统通行密钥,实现在同品牌设备间共享通行密钥。',
},
{
id: 1,
title: '2024.11.2更新',
name: '桌面端快捷键更新',
content: '系统支持查看快捷键列表,并按需修改、重置快捷键。',
},
{
id: 2,
title: '2024.11.3更新',
name: '密码设置调整',
content: '在注册或登录账号时,如尚未设置密码,为了提升账号安全,同时防止因无法获取验证码导致无法登录,需完成密码设置。',
},
{
id: 3,
title: '2024.11.4更新',
name: '注册流程更新',
content:
'注册时,你可使用手机号注册飞书账号,创建你的飞书企业或组织,或加入其他企业或组织,与所在企业同事或组织成员在线沟通、协作编辑文档、共享日程并快速组织会议等,实现高效办公协作。如需注册用于个人使用场景的账号,可在注册流程中的 使用场景 中选择 个人使用,根据实际情况选择 你的职位,然后点击 创建企业或组织,并填写信息。其中,企业或组织名称、行业类型、人员规模 和 所在地区 可任意填写或选择。完成后点击 创建,即可成功注册账号.',
},
{
id: 4,
title: '2024.11.5更新',
name: '通行密钥',
content:
'通行密钥支持全方位的身份验证技术,用户可以将每台手机或电脑等设备的屏幕解锁方式(如:面容识别、指纹识别、锁屏密码等)作为通行密钥,添加完成后,可以通过此方式安全便捷地完成登录或身份验证等操作。',
},
],
});
2. 写出点击锚点导航跳转到页面相应的内容
import { LinkData } from './modal';
const activeLink = ref('');
// 指定滚动的容器
const getContainer = () => {
// 给组件指定渲染的容器,解决锚点不会随页面滚动而移动的问题
return document.querySelector('.container');
};
// 自定义高亮锚点
const getCurrentAnchor = () => {}
//锚点跳转实现方法
const handleClick = (e: Event, link: LinkData) => {
e.preventDefault();
if (link.href) {
// 找到锚点对应的节点
let element = document.getElementById(link.href.replace('#', ''));
activeLink.value = link.href.slice(1);
// 如果对应id的锚点存在,就跳滚动到锚点顶部
if (element) {
element.scrollIntoView({
behavior: 'smooth',
});
}
}
};
// 当滑动页面时,监听统一页面锚点与导航的定位
const onChange = (link: string) => {
activeLink.value = link;
};
3. 写出监听滚动页面时,自动滑动导航的锚点定位
import { debounce } from './debounce '; // 引入防抖函数
let requestAnimationFrameId: number | null = null;
let scrollTimeout;
// 监听滚动页面定位导航
const checkActiveLink = () => {
// 获取页面内容的容器
const sections = document.querySelectorAll('.container');
let current = '';
sections.forEach((section) => {
// offsetTop返回当前元素相对于其父元素的顶部内边距的距离。
const sectionTop = section.offsetTop;
// 返回元素的可视高度,包括内边距(padding),但不包括边框(border)、滚动条或外边距(margin)
const sectionHeight = section.clientHeight;
// 获取滚动的高度
const scrollTop = document.documentElement.scrollTop;
// 计算元素到窗口顶端的距离
const rect = section.getBoundingClientRect();
const distance = rect.top;
// console.log("---111",scrollTop,"---222",sectionTop,"---333",sectionHeight,'---444',distance);
// 当滚动的高度在锚点的页面内容的元素范围之内,并且元素距离窗口的距离超出窗口时成立
if (scrollTop <= (sectionTop + sectionHeight + 5) && distance <= 5 ) {
// 获得到元素的id
current = section.id;
}
});
// 将元素id复制给activeLink,来控制选中锚点的样式。
activeLink.value = current;
};
const debouncedCheckActiveLink = debounce(checkActiveLink, 100, { leading: true, trailing: true });
onMounted(() => {
// targetOffset.value = window.innerHeight ;
window.addEventListener('scroll', ()=>{
// 清除之前的定时器
clearTimeout(scrollTimeout);
// 设置一个新的定时器,在200毫秒后如果没有新的滚动事件,则触发处理函数
scrollTimeout = setTimeout(debouncedCheckActiveLink, 100);
})
});
onUnmounted(() => {
window.removeEventListener('scroll', debouncedCheckActiveLink);
if (requestAnimationFrameId !== null) {
cancelAnimationFrame(requestAnimationFrameId);
}
});
注: 滑动页面时触发多次滚动事件,我们需要做防抖。并且我们需要监听的是滚动之后的数据(滚动一次页面的高度;内容元素距离父元素的距离;内容元素的可视高度;滚动之后内容元素距离窗口顶端的距离),所以我们需要用setTimeout来延迟滚动之后的事件,等100ms之后没有任何滚动动作之后再触发事件。
4. 防抖函数debounced
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number,
options: { leading?: boolean; trailing?: boolean } = {}
): (...args: Parameters<T>) => void {
let timeout: NodeJS.Timeout | null = null;
let result: ReturnType<T> | undefined;
let context: any;
let args: Parameters<T>;
const later = () => {
timeout = null;
if (!options.leading) {
result = func.apply(context, args);
}
};
return function (this: any, ..._args: Parameters<T>): void {
context = this;
args = _args;
if (timeout === null) {
if (options.leading) {
result = func.apply(context, args);
timeout = setTimeout(later, wait);
} else {
timeout = setTimeout(later, wait);
}
} else if (options.trailing) {
clearTimeout(timeout);
timeout = setTimeout(later, wait);
}
};
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)