fix: outline 侧边栏指示标自动滚动跟随
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user