Files
陈春 c9cf644ac5 chore: 添加构建推送脚本并更新项目配置
- 添加 build-and-push.sh 脚本用于自动化构建和推送 Docker 镜像
- 更新 .dockerignore 排除 pnpm-workspace.yaml
- 更新项目依赖和配置文件
2026-06-03 17:19:14 +08:00

89 lines
3.0 KiB
TypeScript
Executable File

import { onBeforeUnmount, watch, type Ref, isRef, unref, computed } from 'vue'
import { useNuxtApp, useHead } from '#imports'
// 按 className 维度追踪各锁的引用计数,解决多组件使用不同 className 并发锁定时的类名泄漏问题
const lockCounts = new Map<string, number>()
export const usePageScrollLock = (
isLocked: Ref<boolean> | 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)
}
})
}