kl-anchor(vue锚点组件)
文章目录要求使用实现要求导航栏内容块连接标记联动效果使用实现
·
示例
功能描述
- 点击左侧导航栏,右侧能滚动到指定的位置
- 右侧滚动,左侧能自动选中
存在问题
- 多次监听
- 直接绑定滚动到了body
使用
这儿是结合element-ui 一起使用的,同学们使用的时候要注意引入对应组件
<template>
<div class="container pr flex-wrap">
<el-menu
:default-active="state.defaultActive"
class="el-menu-vertical-demo width-120"
>
<el-menu-item
:index="item.id"
v-for="item in state.menuList"
:key="item.id"
@click="changenavigationBar(item.id)"
>
<span>{{ item.title }}</span>
</el-menu-item>
</el-menu>
<div class="content">
<h2>我是右侧的顶部内容</h2>
<!-- 必传项为 id id必须和el-menu-item的index一致,activeId函数用于接收滚动到那个id区域了-->
<kl-anchor
class="content-item"
:id="item.id"
v-for="item in state.menuList"
:key="item.id"
@activeId="activeId"
>
{{ item.id }}
</kl-anchor>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive } from "vue-demi";
const state = reactive({
defaultActive: "nav1",
menuList: [
{
id: "nav1",
title: "nav11",
},
{
id: "nav2",
title: "nav22",
},
{
id: "nav3",
title: "nav33",
},
{
id: "nav4",
title: "nav44",
},
{
id: "nav5",
title: "nav55",
},
{
id: "nav6",
title: "nav66",
},
],
});
const activeId = (id: string) => {
if (id !== state.defaultActive) {
state.defaultActive = id;
}
// console.log(id);
};
// 平滑滚动方式
const changenavigationBar = (id: String) => {
var PageId = document.querySelector("#" + id) as HTMLElement;
window.scrollTo({
top: PageId.offsetTop,
behavior: "smooth",
});
};
</script>
<style lang="scss" scoped>
.width-120 {
width: 120px;
}
.content-item {
height: 500px;
}
.el-menu-vertical-demo {
position: fixed;
top: 0;
left: 0;
}
.container {
padding-left: 120px;
}
</style>
实现
kl-anchor
<template>
<div class="kl-anchor" :id="id">
<slot> </slot>
</div>
</template>
<script lang="ts" setup>
import {
onBeforeUnmount,
onMounted,
reactive,
onBeforeUpdate,
nextTick,
} from "vue-demi";
const props = defineProps({
// 这儿适用于监听滚动的id 一定要注意唯一性
id: {
type: String,
required: true,
},
// 精度 建议实际每项高度越小,这儿也传入越小
accuracy: {
type: Number,
default: 150,
},
});
const emits = defineEmits(["activeId"]);
interface i_state {
el: HTMLElement | null;
offsetTop: number;
clientHeight: number;
myHeight: number;
}
const state: i_state = reactive({
el: null, // 节点对象
offsetTop: 0, // 当前节点到body的顶部的距离
clientHeight: 0, // 页面可视区高度
myHeight: 0, // 当前节点的高度
});
function throttle(fn: () => void, delay: number) {
let time1 = 0;
return function () {
let time2 = Date.now();
if (time2 - time1 >= delay) {
fn();
time1 = time2;
}
};
}
const eventScroll = throttle(() => {
let bodyScroll: number = document.documentElement.scrollTop;
if (
bodyScroll - state.offsetTop >= 0 &&
state.offsetTop + state.myHeight > bodyScroll
) {
// console.log(props.id);
emits("activeId", props.id);
}
}, props.accuracy);
function handleEvent() {
state.el = document.querySelector("#" + props.id) as HTMLElement;
state.myHeight = state.el.offsetHeight; //如果是异步获取,将无法获取到没有加载进来的内容的高度
state.offsetTop = state.el.offsetTop; //如果是异步获取,将无法获取到没有加载进来的内容的高度
state.clientHeight = document.body.clientHeight;
// 监听事件 当前是处于哪个nav对应的内容
window.document.addEventListener("scroll", eventScroll);
}
onMounted(() => {
handleEvent();
});
// 当数据更新时的业务
onBeforeUpdate(() => {
nextTick(() => {
handleEvent();
});
});
onBeforeUnmount(() => {
window.document.removeEventListener("scroll", eventScroll);
});
</script>
<style lang="scss" scoped></style>
更多推荐
已为社区贡献2条内容
所有评论(0)