This commit is contained in:
1 parent
8dfe2624ab
commit
3fe68cd57e
2 files changed
+48
-5
No files matched your search
+16
-4
@@ -1405,6 +1405,7 @@ footer section p:first-child {
|
||||
margin-left: -1rem;
|
||||
padding-left: 1rem;
|
||||
position: relative;
|
||||
z-index: 1; /* 保证在指示器上方 */
|
||||
color: #b0b0b0; /* VOID style dark text */
|
||||
transition:
|
||||
color 0.2s,
|
||||
@@ -1421,16 +1422,27 @@ footer section p:first-child {
|
||||
|
||||
.TOC li a.is-active-link {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 动态滑动指示器 */
|
||||
.toc-active-indicator {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.3); /* VOID style dark highlight */
|
||||
border-left: 2px solid #419b38;
|
||||
padding-left: calc(1rem - 2px); /* adjust padding for the border */
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
transition:
|
||||
transform 0.24s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
height 0.24s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
opacity 0.2s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1199.5px) {
|
||||
.TOC li a:hover {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-left: 2px solid #419b38;
|
||||
padding-left: calc(1rem - 2px);
|
||||
/* hover 时不修改背景和左边框了,保持透明给指示器让路 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
|
||||
let sidebarShow = $state(true);
|
||||
let hasToc = $state(false);
|
||||
let indicatorTop = $state(0);
|
||||
let indicatorHeight = $state(0);
|
||||
let indicatorOpacity = $state(0);
|
||||
let tocContainer = $state<HTMLElement>();
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
|
||||
@@ -84,6 +88,29 @@
|
||||
};
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (!hasToc || !tocContainer) return;
|
||||
|
||||
const updateIndicator = () => {
|
||||
const activeLink = tocContainer!.querySelector('a.is-active-link') as HTMLElement;
|
||||
if (activeLink) {
|
||||
indicatorTop = activeLink.offsetTop;
|
||||
indicatorHeight = activeLink.offsetHeight;
|
||||
indicatorOpacity = 1;
|
||||
} else {
|
||||
indicatorOpacity = 0;
|
||||
}
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(updateIndicator);
|
||||
observer.observe(tocContainer, { attributes: true, subtree: true, attributeFilter: ['class'] });
|
||||
|
||||
setTimeout(updateIndicator, 100);
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
});
|
||||
$effect(() => {
|
||||
if (hasToc && sidebarShow) {
|
||||
document.body.classList.add('sidebar-show');
|
||||
@@ -176,7 +203,11 @@
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div class="toc-mask" onclick={() => (sidebarShow = false)}></div>
|
||||
<div class="TOC" aria-label="{$page.data.aliases?.post || '文章'}目录">
|
||||
<div class="TOC" bind:this={tocContainer} aria-label="{$page.data.aliases?.post || '文章'}目录">
|
||||
<div
|
||||
class="toc-active-indicator"
|
||||
style="transform: translateY({indicatorTop}px); height: {indicatorHeight}px; opacity: {indicatorOpacity}"
|
||||
></div>
|
||||
<div class="js-toc"></div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in new issue
Block a user