From 298961e43f5aa7a21a10b43b87df67b2a57e948c Mon Sep 17 00:00:00 2001 From: Kaxi <1042864399@qq.com> Date: Sun, 31 May 2026 21:28:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=BB=9A=E5=8A=A8?= =?UTF-8?q?=E9=87=8D=E7=BD=AE=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- plugins/scroll-reset.client.ts | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 plugins/scroll-reset.client.ts diff --git a/plugins/scroll-reset.client.ts b/plugins/scroll-reset.client.ts new file mode 100644 index 0000000..08983e3 --- /dev/null +++ b/plugins/scroll-reset.client.ts @@ -0,0 +1,44 @@ +export default defineNuxtPlugin(() => { + if (typeof window === 'undefined') return + + let observer: IntersectionObserver | null = null + let headings: HTMLElement[] = [] + + function initScrollSpy() { + // 清理旧的 observer + if (observer) observer.disconnect() + + // 收集所有带 id 的标题 + headings = Array.from(document.querySelectorAll('.prose h2[id], .prose h3[id]')) as HTMLElement[] + if (headings.length === 0) return + + observer = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + const id = entry.target.getAttribute('id') + if (id) { + history.replaceState(null, '', `#${id}`) + } + } + } + }, + { rootMargin: '-80px 0px -70% 0px' } + ) + + headings.forEach(h => observer!.observe(h)) + } + + // 路由切换后重新初始化 + const router = useRouter() + router.afterEach(() => { + nextTick(() => { + setTimeout(initScrollSpy, 100) + }) + }) + + // 首次加载 + onMounted(() => { + setTimeout(initScrollSpy, 100) + }) +})