Files
arcticnewone/app/components/layouts/ArcticNewOneHeader.vue
T
virtheart 449010718e feat(header): 添加导航菜单和响应式布局
- 新增导航菜单数据配置
- 实现左侧logo和标题区域
- 添加中间导航链接区域
- 添加右侧菜单图标
- 优化header布局为flex响应式设计
2026-04-26 22:19:10 +08:00

141 lines
4.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue'
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
// 导航菜单数据配置
const navItems = [
{ name: 'Home', path: '/' },
{ name: 'Studio', path: '/studio' },
{ name: 'Work', path: '/work' },
{ name: 'News', path: '/news' },
{ name: 'Contact', path: '/contact' }
]
const topHeaderRef = ref<HTMLElement | null>(null)
const bottomHeaderRef = ref<HTMLElement | null>(null)
let topHeaderTween: gsap.core.Tween | null = null
let bottomHeaderScrollTrigger: ReturnType<typeof ScrollTrigger.create> | null = null
let isBottomHeaderVisible = false
const setBottomHeaderVisible = (visible: boolean) => {
if (!bottomHeaderRef.value || isBottomHeaderVisible === visible) {
return
}
isBottomHeaderVisible = visible
gsap.to(bottomHeaderRef.value, {
yPercent: visible ? 0 : 140,
autoAlpha: visible ? 1 : 0,
duration: 0.35,
ease: 'power2.out',
overwrite: 'auto',
})
}
onMounted(() => {
// 底部导航默认藏在视口下方,只有滚动到深处并向上滚时才出现。
gsap.set(bottomHeaderRef.value, {
yPercent: 140,
autoAlpha: 0,
})
// 顶部导航在页面刚开始滚动的前 200px 内逐渐上移并隐藏。
topHeaderTween = gsap.to(topHeaderRef.value, {
yPercent: -140,
autoAlpha: 0,
ease: 'none',
scrollTrigger: {
start: 0,
end: 200,
scrub: true,
},
})
bottomHeaderScrollTrigger = ScrollTrigger.create({
start: 0,
end: 'max',
onUpdate: (self) => {
const scrollY = self.scroll()
// 深度超过 2500px 后向上滚,显示未来用于深层内容的底部导航。
if (scrollY > 2500 && self.direction === -1) {
setBottomHeaderVisible(true)
return
}
// 回到 2300px 以内,或继续向下滚时,底部导航收回。
if (scrollY <= 2300 || self.direction === 1) {
setBottomHeaderVisible(false)
}
},
})
ScrollTrigger.refresh()
})
onBeforeUnmount(() => {
topHeaderTween?.scrollTrigger?.kill()
topHeaderTween?.kill()
bottomHeaderScrollTrigger?.kill()
topHeaderTween = null
bottomHeaderScrollTrigger = null
isBottomHeaderVisible = false
gsap.killTweensOf([topHeaderRef.value, bottomHeaderRef.value])
})
</script>
<template>
<header
ref="topHeaderRef"
class="site-header-top fixed left-0 right-0 top-0 z-50 mx-auto flex h-[70px] w-page-container max-w-[calc(100%-2rem)] items-center justify-between px-4 py-5"
>
<!-- 左侧Logo 标题 -->
<div class="flex items-center gap-3">
<div class="logo-icon flex h-8 w-8 items-center justify-center rounded-full bg-black text-white">
<!-- 简单的极简风格 SVG Logo -->
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="h-5 w-5">
<circle cx="12" cy="12" r="10" />
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
<path d="M2 12h20" />
</svg>
</div>
<div class="flex flex-col justify-center">
<span class="text-sm font-bold leading-none tracking-wider text-black">北极新一</span>
<span class="mt-1 text-[10px] leading-none text-gray-500">ArcticNewOne</span>
</div>
</div>
<!-- 中间导航链接 -->
<nav class="hidden md:flex items-center gap-8">
<a
v-for="item in navItems"
:key="item.name"
:href="item.path"
class="group relative flex items-center text-sm font-medium text-black"
>
<span class="absolute -left-3 h-1.5 w-1.5 rounded-full bg-black opacity-0 transition-all duration-300 group-hover:opacity-100"></span>
<span class="transition-transform duration-300 group-hover:translate-x-2">{{ item.name }}</span>
</a>
</nav>
<!-- 右侧菜单图标 (使用 div 绘制) -->
<div class="menu-icon group flex h-8 w-8 cursor-pointer flex-col items-end justify-center gap-[6px]">
<div class="h-[2px] w-6 bg-black transition-all duration-300 group-hover:translate-y-[1px]"></div>
<div class="h-[2px] w-6 bg-black transition-all duration-300 group-hover:-translate-y-[1px]"></div>
</div>
</header>
<header
ref="bottomHeaderRef"
class="fixed bottom-0 left-0 right-0 z-50 mx-auto h-[70px] w-page-container max-w-[calc(100%-2rem)] bg-blue-200 px-4 py-5"
style="visibility: hidden; opacity: 0;"
>
<nav>Bottom Navigation</nav>
</header>
</template>