# 吸顶导航 Sticky Header > 往下滚时导航栏一直贴在最上面,滚一点后变矮,背景变成半透明毛玻璃。 分类:导航 [全部组件和流程](https://www.dachuiai.com/llms.txt) · [导航选型规则](https://www.dachuiai.com/guides/navigation.md) ## 给 AI:用户把这个组件发给了你 1. 先看用户的项目:框架、页面类型、现在的风格、手机还是电脑为主。 2. 对照下面的「适合 / 不适合」判断合不合适。不合适就直接说,并从[导航选型规则](https://www.dachuiai.com/guides/navigation.md)或[全部组件](https://www.dachuiai.com/llms.txt)里另挑一个。 3. 合适就按「装进项目」装好,示例里的文字和图片换成用户自己的。 4. 告诉用户用了什么、放在哪个文件、以后想改快慢、颜色、大小改哪个参数。 ## 什么时候用 - 适合:内容长、要随时能点导航的页面:官网、电商、内容站。 - 不适合:一屏就看完的落地页;导航很高、会挡住大片内容的手机页面。 - 适合的网站:官网、本地小店、电商、作品集、SaaS、内容站 - 放在:导航 - 手机上:好用:高度控制在 56px 以内,别挡内容。 - 性能代价:低:变矮用 transform,不改高度。 - 常搭配:[顶部导航栏 Navbar](https://www.dachuiai.com/c/navbar.md)、[返回顶部 Back to Top](https://www.dachuiai.com/c/back-to-top.md)、[锚点导航 Anchor](https://www.dachuiai.com/c/anchor-nav.md) ## 装进项目 - 写法:React 18 以上 + TypeScript + Tailwind CSS v4,一个文件就能用,不引用别的组件。 - 依赖:`npm i lucide-react` - 文件:原样存成 `sticky-header.tsx`(比如 `src/components/sticky-header.tsx`)。能跑命令就下载[原文](https://www.dachuiai.com/code/sticky-header.tsx),比手抄准:`curl -fsSL -o src/components/sticky-header.tsx https://www.dachuiai.com/code/sticky-header.tsx`(Windows 写 `curl.exe`)。下载完看一眼第一行是 `import`,不是网页。 - 导出:`useScrolled`、`StickyHeader` 是可以直接用的部分;最后的 `export default` 是演示示例,只作参考。 - 图片:示例用了本站的 [/tex/s5.jpg](https://www.dachuiai.com/tex/s5.jpg)、[/tex/s6.jpg](https://www.dachuiai.com/tex/s6.jpg)、[/tex/s3.jpg](https://www.dachuiai.com/tex/s3.jpg)、[/tex/s2.jpg](https://www.dachuiai.com/tex/s2.jpg)、[/tex/s1.jpg](https://www.dachuiai.com/tex/s1.jpg)、[/tex/s4.jpg](https://www.dachuiai.com/tex/s4.jpg)。换成用户自己的图;只想先看效果,可以从本站同样的路径下载,放进项目的 public 目录。 - 注意:StickyHeader 是示例用的浅色导航,菜单和品牌写死在文件里。给自己的网站用时,只拿 useScrolled(阈值, 滚动盒子) 判断滚没滚过,导航条按自己的风格写:滚过后变矮、背景变半透明加模糊。 - 钉住效果靠 `position: sticky`:外面任何一层写了 `overflow: hidden` 或 `overflow-x: hidden`(常见在 body、main 上)都会失效,要防横向滚动条改用 `overflow-x: clip`。 - 保留代码里 `prefers-reduced-motion` 的处理:用户开了「减弱动态效果」时内容照样完整显示。 - Next.js(有 app 文件夹):文件第一行加 `'use client';`。 - 用 JavaScript 不用 TypeScript:Vite 和 Next.js 都能直接用 .tsx 文件;也可以删掉类型标注,改名 .jsx。 - 没有 Tailwind:先装 Tailwind CSS v4(Vite 项目 `npm i tailwindcss @tailwindcss/vite`,vite 配置的 plugins 里加 `tailwindcss()`,入口引入的那个 CSS 文件(一般是 `src/index.css`)第一行写 `@import "tailwindcss";`),或者把类名改写成项目用的样式。 - 已经装了 Tailwind v3:别为了一个组件升级。类名大多通用,少数 v4 写法要换,比如焦点框 `outline-2` 在 v3 里要写成 `outline outline-2`。 - 不是 React(Vue、Svelte、小程序、纯 HTML):照着代码的做法改写,时长、曲线、参数保持一样。Wix、有赞这类建站平台装不进代码,告诉用户去平台自带的动效设置里找。 ## 参数 **useScrolled** | 参数 | 默认值 | 作用 | | --- | --- | --- | | `threshold` | `24` | 滚过多少像素才算滚动了 | | `scroller` | | 滚动盒子的 ref;整页滚动时不传 | **StickyHeader** | 参数 | 默认值 | 作用 | | --- | --- | --- | | `scroller` | | 滚动盒子的 ref;整页滚动时不传 | ## 你可以说 > 导航栏做成 Sticky Header 吸顶:往下滚时变矮,加毛玻璃背景。 ## 完整代码 ```tsx import {useEffect, useRef, useState, type RefObject} from 'react'; import {Backpack, Bed, Flame, Lamp, Search, ShoppingBag, Tent, Thermometer} from 'lucide-react'; /* --------------------------------------------------------------- * StickyHeader 吸顶导航 * 页面往下滚的时候,导航栏一直贴在最上面;滚动一点后变矮,背景变成半透明毛玻璃。 * 变矮用 transform 做(背景层往上收、内容缩小一点),不改高度,滚动时不卡。 * 整页用时 scroller 不传;放在可滚动的盒子里时,传这个盒子。 * ------------------------------------------------------------- */ export function useScrolled(threshold = 24, scroller?: RefObject) { const [on, setOn] = useState(false); useEffect(() => { const el = scroller?.current; const read = () => setOn((el ? el.scrollTop : window.scrollY) > threshold); const target = el ?? window; read(); target.addEventListener('scroll', read, {passive: true}); return () => target.removeEventListener('scroll', read); }, [threshold, scroller]); return on; } export function StickyHeader({scroller}: {scroller?: RefObject}) { const compact = useScrolled(24, scroller); return (
{/* 背景层:滚动后往上收 16px,变成毛玻璃 */}
山野
); } /* ---------------- 示例:一个能滚动的商品页 ---------------- */ const GOODS = [['轻量双人帐篷', 899, '/tex/s5.jpg', Tent], ['徒步背包 40L', 459, '/tex/s6.jpg', Backpack], ['羽绒睡袋', 699, '/tex/s3.jpg', Bed], ['卡式炉', 239, '/tex/s2.jpg', Flame], ['露营灯', 159, '/tex/s1.jpg', Lamp], ['保温壶', 129, '/tex/s4.jpg', Thermometer]] as const; export default function StickyHeaderDemo() { const box = useRef(null); return (

秋季露营节

全场满 500 减 80

{['热门装备', '新品上架', '店长推荐'].map((title, k) => (

{title}

{GOODS.slice(k, k + 3).map(([n, p, img, Icon]) => (
{n}
¥{p}
))}
))}
); } ```