feat(mdAnchor):页面滚动时对应锚点标签跟随高亮;点击锚点标签平滑滚动
This commit is contained in:
parent
0b09de219e
commit
52beb5e9b7
@ -2,29 +2,30 @@
|
||||
<aside :class="classAside">
|
||||
<div class="lay-aside-top">
|
||||
<lay-button
|
||||
@click="handlerBtnClick()"
|
||||
type="primary"
|
||||
size="xs"
|
||||
:class="classAsideBtn"
|
||||
@click="handlerBtnClick()"
|
||||
>
|
||||
<lay-icon :type="iconType" size="40"> </lay-icon>
|
||||
</lay-button>
|
||||
</div>
|
||||
<lay-scroll
|
||||
class="layui-side-scroll-bar layui-side-scroll::-webkit-scrollbar" >
|
||||
class="layui-side-scroll-bar layui-side-scroll::-webkit-scrollbar"
|
||||
>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(item, index) in anchorsComput"
|
||||
v-for="(anchor, index) in anchorList"
|
||||
:key="index"
|
||||
class="lay-aside-list"
|
||||
:class="{ active: index === curridx }"
|
||||
@click="curridx = index"
|
||||
:class="{ active: index === activeIndex }"
|
||||
@click.prevent="handlerListItemClick(index, anchor)"
|
||||
>
|
||||
<a
|
||||
:href="`#${item}`"
|
||||
:href="`#${anchor}`"
|
||||
class="lay-aside-link"
|
||||
:class="{ active: index === curridx }"
|
||||
>{{ item }}</a
|
||||
:class="{ active: index === activeIndex }"
|
||||
>{{ anchor }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
@ -32,21 +33,25 @@
|
||||
</aside>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import { computed, onMounted, ref, shallowRef } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
anchors?: Array<string> | string;
|
||||
currIndex: number;
|
||||
show: boolean | string;
|
||||
show: boolean;
|
||||
}>();
|
||||
|
||||
let curridx = ref(props.currIndex);
|
||||
const show = ref(props.show);
|
||||
const iconType = ref("layui-icon-right");
|
||||
const anchor = props.anchors;
|
||||
let activeIndex = ref<number>(0);
|
||||
const show = ref<boolean>(props.show);
|
||||
const iconType = ref<string>("layui-icon-right");
|
||||
const anchors: string | string[] | undefined = props.anchors;
|
||||
/**滚动条高度 */
|
||||
const scrollTop = ref<number>(0);
|
||||
/**要监听的滚动元素 */
|
||||
const scrollRefEl = shallowRef<HTMLElement | undefined>(undefined);
|
||||
|
||||
const anchorsComput = computed(() => {
|
||||
return typeof anchor === "string" ? anchor?.split(",") : anchor;
|
||||
const anchorList = computed(() => {
|
||||
return typeof anchors === "string" ? anchors?.split(",") : anchors;
|
||||
});
|
||||
|
||||
const classAside = computed(() => [
|
||||
@ -63,10 +68,68 @@ const handlerBtnClick = () => {
|
||||
show.value = !show.value;
|
||||
iconType.value = show.value ? "layui-icon-right" : "layui-icon-left";
|
||||
};
|
||||
|
||||
const handlerListItemClick = (index: number, id: string) => {
|
||||
activeIndex.value = index;
|
||||
scrollToTitle(id);
|
||||
};
|
||||
|
||||
const handlerScroll = () => {
|
||||
// 距离顶部 90 改变 activeIndex
|
||||
scrollTop.value = getScrollTop(scrollRefEl.value) + 90;
|
||||
anchorList.value?.forEach((item, index) => {
|
||||
const elOffsetTop = document.getElementById(item)?.offsetTop;
|
||||
if (elOffsetTop) {
|
||||
if (index === 0 && scrollTop.value < elOffsetTop) {
|
||||
activeIndex.value = 0;
|
||||
} else if (scrollTop.value >= elOffsetTop) {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// TODO 封装 hooks
|
||||
scrollRefEl.value = document.querySelector(".layui-body");
|
||||
if (!scrollRefEl.value) throw new Error("");
|
||||
scrollRefEl.value.scrollTop = 0;
|
||||
scrollRefEl.value?.addEventListener("scroll", throttle(handlerScroll, 500));
|
||||
});
|
||||
|
||||
/**获取滚动高度 */
|
||||
const getScrollTop = (el: HTMLElement | undefined): number => {
|
||||
return el
|
||||
? el.scrollTop
|
||||
: window.pageYOffset ||
|
||||
document.documentElement.scrollTop ||
|
||||
document.body.scrollTop ||
|
||||
0;
|
||||
}
|
||||
/**平滑滚动 */
|
||||
const scrollToTitle = (id: string): void =>{
|
||||
document.getElementById(id)?.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "start",
|
||||
inline: "nearest",
|
||||
});
|
||||
}
|
||||
|
||||
const throttle = (func: Function, wait: number) => {
|
||||
var timer: any = null;
|
||||
return (...args: any) => {
|
||||
if (!timer) {
|
||||
timer = setTimeout(() => {
|
||||
timer = null;
|
||||
func.apply(this, args);
|
||||
}, wait);
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.layui-side-scroll-bar{
|
||||
overflow-y: scroll;
|
||||
.layui-side-scroll-bar {
|
||||
overflow-y: scroll;
|
||||
max-width: 156px;
|
||||
}
|
||||
.layui-side-scroll::-webkit-scrollbar {
|
||||
@ -155,7 +218,7 @@ const handlerBtnClick = () => {
|
||||
}
|
||||
}
|
||||
.lay-aside-collapse-btn-collapse {
|
||||
right:0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
@ -171,8 +234,8 @@ const handlerBtnClick = () => {
|
||||
.lay-aside-list {
|
||||
max-width: 68px;
|
||||
}
|
||||
.layui-side-scroll-bar{
|
||||
.layui-side-scroll-bar {
|
||||
max-width: 68px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user