import { onBeforeUnmount, watch, type Ref, isRef, unref } from 'vue' import { useNuxtApp, useHead } from '#imports' // 全局模块级变量,用于记录锁定请求的次数,解决多组件并发锁定的竞态问题 let lenisLockCount = 0 export const usePageScrollLock = ( isLocked: Ref | boolean, options?: { className?: string } ) => { const nuxtApp = useNuxtApp() // 由于 $lenis 是从 client 插件中注入的,SSR 期间可能为 undefined // 而且在 setup 期间解构可能拿不到最新的值,我们改为在函数内部动态获取 useHead({ htmlAttrs: { lang: 'zh-CN', }, }) const lockClassName = options?.className ?? 'is-home-intro-active' let currentlyLocked = false const updateLock = (locked: boolean) => { // 防止重复触发相同状态 if (locked === currentlyLocked) return currentlyLocked = locked // Lenis 控制仅在客户端有效,不需要依赖 $lenis 立即存在 if (import.meta.client) { if (locked) { lenisLockCount++ // 只有第一个锁请求时,才真正停止 Lenis 并滚动到顶部 if (lenisLockCount === 1) { document.documentElement.classList.add(lockClassName) // 注意:需要在这里动态获取 $lenis,因为此时 client 插件才可能挂载完成 const lenisInstance = nuxtApp.$lenis if (lenisInstance) { lenisInstance.scrollTo(0, { immediate: true }) lenisInstance.stop() } } } else { lenisLockCount = Math.max(0, lenisLockCount - 1) // 只有当所有锁请求都释放时,才重新启动 Lenis if (lenisLockCount === 0) { document.documentElement.classList.remove(lockClassName) // 同理,动态获取 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) } }) }