fix: outline 侧边栏指示标自动滚动跟随

This commit is contained in:
sanbuphy
2026-02-07 09:34:09 +08:00
parent 03c63c7015
commit c3fc76a32f
2 changed files with 134 additions and 0 deletions
+85
View File
@@ -85,8 +85,93 @@ onMounted(() => {
applyFontSize(saved) applyFontSize(saved)
applyLineHeight(savedLineHeight) applyLineHeight(savedLineHeight)
isHydrated.value = true isHydrated.value = true
// 初始化 outline 自动滚动功能
initOutlineAutoScroll()
}) })
// ============================================
// Outline 侧边栏自动滚动跟随功能
// 当页面滚动时,自动滚动 outline 让当前激活项保持在可视区域
// ============================================
function initOutlineAutoScroll() {
// 使用 MutationObserver 监听 outline 的变化
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const target = mutation.target
if (target.classList.contains('active') && target.tagName === 'A') {
scrollOutlineToActiveItem(target)
}
}
}
})
// 开始监听
const startObserving = () => {
const outlineContainer = document.querySelector('.VPDocAsideOutline')
if (outlineContainer) {
observer.observe(outlineContainer, {
attributes: true,
subtree: true,
attributeFilter: ['class']
})
}
}
// 页面加载完成后开始监听
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startObserving)
} else {
startObserving()
}
// 同时监听路由变化(VitePress 是 SPA
const originalPushState = history.pushState
const originalReplaceState = history.replaceState
history.pushState = function (...args) {
originalPushState.apply(this, args)
setTimeout(startObserving, 100)
}
history.replaceState = function (...args) {
originalReplaceState.apply(this, args)
setTimeout(startObserving, 100)
}
window.addEventListener('popstate', () => {
setTimeout(startObserving, 100)
})
}
// 滚动 outline 让当前激活项保持在可视区域中心
function scrollOutlineToActiveItem(activeLink) {
const outlineContainer = document.querySelector('.VPDocAsideOutline')
if (!outlineContainer || !activeLink) return
const containerRect = outlineContainer.getBoundingClientRect()
const linkRect = activeLink.getBoundingClientRect()
// 计算链接相对于容器的位置
const linkTop = linkRect.top - containerRect.top + outlineContainer.scrollTop
const linkHeight = linkRect.height
const containerHeight = containerRect.height
// 判断链接是否在可视区域外
const isAbove = linkRect.top < containerRect.top + 20
const isBelow = linkRect.bottom > containerRect.bottom - 20
if (isAbove || isBelow) {
// 将激活项滚动到容器中间位置
const targetScrollTop = linkTop - containerHeight / 2 + linkHeight / 2
outlineContainer.scrollTo({
top: targetScrollTop,
behavior: 'smooth'
})
}
}
watch(fontSize, (next) => { watch(fontSize, (next) => {
if (!isHydrated.value) return if (!isHydrated.value) return
const normalized = clampFontSize(next) const normalized = clampFontSize(next)
+49
View File
@@ -345,3 +345,52 @@
padding-bottom: 18vh !important; padding-bottom: 18vh !important;
box-sizing: border-box; box-sizing: border-box;
} }
/* ============================================
Outline 侧边栏指示标滚动修复
问题:当页面内容很长时,outline 中的指示标(marker)
会移动到可见区域之外,用户看不到当前阅读位置
============================================ */
/* 让 outline 容器可以独立滚动 */
.VPDocAsideOutline {
max-height: calc(100vh - var(--vp-nav-height, 64px) - 32px);
overflow-y: auto;
overflow-x: hidden;
}
/* 隐藏滚动条但保持滚动功能 */
.VPDocAsideOutline::-webkit-scrollbar {
width: 4px;
}
.VPDocAsideOutline::-webkit-scrollbar-track {
background: transparent;
}
.VPDocAsideOutline::-webkit-scrollbar-thumb {
background: var(--vp-c-divider);
border-radius: 2px;
}
.VPDocAsideOutline::-webkit-scrollbar-thumb:hover {
background: var(--vp-c-text-3);
}
/* Firefox 滚动条样式 */
.VPDocAsideOutline {
scrollbar-width: thin;
scrollbar-color: var(--vp-c-divider) transparent;
}
/* 确保 outline 内容区域正确显示 */
.VPDocAsideOutline .content {
position: relative;
min-height: min-content;
}
/* 确保 marker 指示标始终在可见区域内 */
.VPDocAsideOutline.has-outline .outline-marker {
position: absolute;
will-change: top;
}