import { onBeforeUnmount, watch, type Ref, isRef, unref, computed } from 'vue' import { useNuxtApp, useHead } from '#imports' // 按 className 维度追踪各锁的引用计数,解决多组件使用不同 className 并发锁定时的类名泄漏问题 const lockCounts = new Map() export const usePageScrollLock = ( isLocked: Ref | boolean, options?: { className?: string } ) => { const nuxtApp = useNuxtApp() // 由于 $lenis 是从 client 插件中注入的,SSR 期间可能为 undefined // 而且在 setup 期间解构可能拿不到最新的值,我们改为在函数内部动态获取 const lockClassName = options?.className ?? 'is-home-intro-active' // SSR 和 CSR 都支持:自动将 class 注入到 HTML 标签上 // 多实例调用时 Unhead 会合并各自的 class 值 useHead({ htmlAttrs: { class: computed(() => unref(isLocked) ? lockClassName : ''), lang: 'zh-CN', }, }) let currentlyLocked = false const updateLock = (locked: boolean) => { // 防止重复触发相同状态 if (locked === currentlyLocked) return currentlyLocked = locked // Lenis 控制仅在客户端有效,不需要依赖 $lenis 立即存在 if (import.meta.client) { if (locked) { const prevCount = lockCounts.get(lockClassName) ?? 0 lockCounts.set(lockClassName, prevCount + 1) // 该 className 首次被锁定时,添加对应类名 if (prevCount === 0) { document.documentElement.classList.add(lockClassName) } // 所有 className 的锁计数总和为 1 时(即全局第一个锁),停止 Lenis 并滚到顶部 const totalLocks = Array.from(lockCounts.values()).reduce((sum, c) => sum + c, 0) if (totalLocks === 1) { const lenisInstance = nuxtApp.$lenis if (lenisInstance) { lenisInstance.scrollTo(0, { immediate: true }) lenisInstance.stop() } } } else { const prevCount = lockCounts.get(lockClassName) ?? 0 const newCount = Math.max(0, prevCount - 1) lockCounts.set(lockClassName, newCount) // 该 className 的锁计数归零时,移除对应类名 if (newCount === 0) { document.documentElement.classList.remove(lockClassName) } // 所有 className 的锁计数总和为 0 时(即全局最后一个锁释放),重启 Lenis const totalLocks = Array.from(lockCounts.values()).reduce((sum, c) => sum + c, 0) if (totalLocks === 0) { const lenisInstance = nuxtApp.$lenis if (lenisInstance) { lenisInstance.start() } } } } } // 监听 Ref 变化,或立即处理 boolean 值 if (isRef(isLocked)) { watch(isLocked, updateLock, { immediate: true }) } else { updateLock(isLocked) } // 组件卸载时安全释放当前持有的锁 onBeforeUnmount(() => { if (currentlyLocked) { updateLock(false) } }) }