All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 5m17s
- Navigation: fixed + transparent over Hero (home) / at page top (other routes); fades to glass-on-scroll. Glass uses surface tone matching site cards. - Filter bar sticky glass synced to nav recipe (no seam between layers). - HeroBanner: full-viewport video, center title removed, bottom dim overlay removed, eyebrow/countdown repositioned below the nav. - ArtistDetail: removed portrait shadow; added FloatingBackButton that uses router.back() with internal-history fallback to /. - Floating buttons (back + vote) translateY upward to avoid footer rather than disappearing, via useFooterPush. - Home: useScrollRestore preserves scroll position on return from detail pages; temporarily disables scroll-snap during restore. - PerformanceVideo: max-w capped by 85svh*16/9 so small viewports never crop. - ArtistFilters: hide horizontal scrollbar thumb in tag container. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
103 lines
3.8 KiB
TypeScript
103 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { usePathname } from "next/navigation";
|
|
import Logo from "./Logo";
|
|
import NavLinks from "./NavLinks";
|
|
import SearchTrigger from "./SearchTrigger";
|
|
import AuthMenu from "./auth/AuthMenu";
|
|
import RemainingVotesBadge from "./auth/RemainingVotesBadge";
|
|
import { cn } from "@/lib/cn";
|
|
|
|
/**
|
|
* 导航栏 · 上下文敏感的玻璃态切换
|
|
* - 顶部/盖在 Hero 之上时:完全透明,只有内容(无玻璃层、无分割线)
|
|
* - 内容滚到 nav 下方时:渐显为毛玻璃(blur + saturate + surface 微着色),与下方筛选条无缝连接
|
|
* - 玻璃层用独立 absolute 子层 + opacity 过渡,避免直接切 backdrop-filter class 的硬切
|
|
*
|
|
* 检测策略:
|
|
* - 路由存在 [data-hero-banner] → IntersectionObserver 观察 Hero(首页)
|
|
* - 否则 → 滚动监听,scrollY <= 0 时透明,任意向下滚动即玻璃
|
|
*/
|
|
export default function Navigation() {
|
|
const pathname = usePathname();
|
|
// 初始乐观:任何页面首屏都假设在顶部/Hero 上,透明。effect 挂载后会立刻校正。
|
|
const [isTransparent, setIsTransparent] = useState(true);
|
|
|
|
// 维护一个站内导航计数器(per-tab),供 FloatingBackButton 判断 router.back() 是否安全
|
|
useEffect(() => {
|
|
const prev = parseInt(sessionStorage.getItem("nav:count") || "0", 10);
|
|
sessionStorage.setItem("nav:count", String(prev + 1));
|
|
}, [pathname]);
|
|
|
|
useEffect(() => {
|
|
const heroEl = document.querySelector("[data-hero-banner]");
|
|
|
|
// 1) 有 Hero 的页面:观察 Hero 是否还在视口顶部下方
|
|
if (heroEl) {
|
|
setIsTransparent(true);
|
|
const observer = new IntersectionObserver(
|
|
([entry]) => setIsTransparent(entry.isIntersecting),
|
|
{
|
|
// 视口顶部下移 80px(导航高度),让 Hero 完全滚出 nav 下方时触发
|
|
rootMargin: "-80px 0px 0px 0px",
|
|
threshold: 0,
|
|
},
|
|
);
|
|
observer.observe(heroEl);
|
|
return () => observer.disconnect();
|
|
}
|
|
|
|
// 2) 无 Hero 的页面:scrollY === 0 时透明,任何向下滚动即玻璃
|
|
const onScroll = () => setIsTransparent(window.scrollY <= 0);
|
|
onScroll();
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
return () => window.removeEventListener("scroll", onScroll);
|
|
}, [pathname]);
|
|
|
|
return (
|
|
<header className="fixed top-0 inset-x-0 z-50">
|
|
{/* 玻璃层 · 通过 opacity 渐显渐隐 · bg-surface 与全站卡片同色 */}
|
|
<div
|
|
aria-hidden
|
|
className={cn(
|
|
"absolute inset-0 bg-surface/40 backdrop-blur-xl backdrop-saturate-150 transition-opacity duration-300",
|
|
isTransparent ? "opacity-0" : "opacity-100",
|
|
)}
|
|
/>
|
|
{/* 顶部 1px 高光 · 仅玻璃态下显示 */}
|
|
<div
|
|
aria-hidden
|
|
className={cn(
|
|
"absolute inset-x-0 top-0 h-px bg-white/[0.06] transition-opacity duration-300",
|
|
isTransparent ? "opacity-0" : "opacity-100",
|
|
)}
|
|
/>
|
|
<nav className="relative max-w-[1500px] mx-auto h-20 px-4 sm:px-6 lg:px-8 flex items-center gap-8">
|
|
<Logo size="md" />
|
|
|
|
{/* 中部:首页 / 排行榜 / 我的 */}
|
|
<NavLinks className="hidden md:flex" />
|
|
|
|
{/* 右侧:搜索 + 今日余票 + 登录/注册 (或 头像+下拉) */}
|
|
<div className="ml-auto flex items-center gap-3">
|
|
<SearchTrigger />
|
|
<RemainingVotesBadge />
|
|
<AuthMenu />
|
|
</div>
|
|
</nav>
|
|
|
|
{/* 移动端:单独一行 nav links · 顶部分割线只在玻璃态显示 */}
|
|
<NavLinks
|
|
className={cn(
|
|
"relative md:hidden overflow-x-auto transition-colors duration-300",
|
|
isTransparent
|
|
? "border-t border-transparent"
|
|
: "border-t border-white/[0.05]",
|
|
)}
|
|
mobile
|
|
/>
|
|
</header>
|
|
);
|
|
}
|