You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Next.js(App Router)+TailwindCSS中实现底部固定转顶部粘性的平滑过渡导航栏

在Next.js(App Router)+TailwindCSS中实现底部固定转顶部粘性的平滑过渡导航栏

嘿,我完全懂这种跳变的难受!之前做类似导航栏时也踩过这个坑——直接切换fixed和sticky类就像突然“瞬移”,完全破坏了页面的流畅感。问题出在这两个定位属性属于不同的布局上下文,浏览器切换时会立刻重排,自然就会跳。

下面是我摸索出来的平滑过渡方案,核心是用固定定位+transform偏移代替类名切换,让导航栏的位置变化是连续的,再结合Tailwind的过渡类实现丝滑动画:

解决思路

  1. 全程用fixed定位(避免切换定位类型导致的布局跳变),初始时固定在视口底部
  2. 监听滚动距离,用transform: translateY()动态调整导航栏的垂直位置,而不是直接改定位类
  3. 分三个滚动阶段处理导航栏位置:
    • 滚动未超过第一屏高度:导航栏保持在底部
    • 滚动进入第二屏的过渡区间:导航栏随滚动逐渐向上移动
    • 滚动完全进入第二屏:导航栏固定在顶部

具体实现代码

首先,导航栏组件需要标记为客户端组件(因为要用到滚动监听和状态),在navbar.tsx顶部加上'use client'指令:

'use client';

import { useState, useEffect, useRef } from 'react';
import Image from 'next/image';

export default function Navbar() {
  // 存储导航栏的transform偏移值
  const [navTransform, setNavTransform] = useState('translateY(0)');
  // 导航栏元素的ref,用于获取自身高度
  const navbarRef = useRef<HTMLElement>(null);
  // 存储导航栏高度
  const [navbarHeight, setNavbarHeight] = useState(0);

  useEffect(() => {
    // 获取导航栏高度
    if (navbarRef.current) {
      setNavbarHeight(navbarRef.current.offsetHeight);
    }

    // 滚动监听函数
    const handleScroll = () => {
      const scrollY = window.scrollY;
      // 第一屏的高度(假设你的第一屏section有id="page1",记得给对应元素加上这个id)
      const page1Height = document.getElementById('page1')?.offsetHeight || window.innerHeight;
      // 过渡触发的阈值:当滚动到第一屏剩余高度等于导航栏高度时开始移动
      const transitionStart = page1Height - navbarHeight;

      if (scrollY <= transitionStart) {
        // 未到过渡点,保持在底部
        setNavTransform('translateY(0)');
      } else if (scrollY <= page1Height) {
        // 过渡区间:计算需要向上移动的距离
        const moveDistance = scrollY - transitionStart;
        setNavTransform(`translateY(-${moveDistance}px)`);
      } else {
        // 完全进入第二屏,固定在顶部
        setNavTransform(`translateY(-${page1Height}px)`);
      }
    };

    // 监听滚动事件
    window.addEventListener('scroll', handleScroll);
    // 初始化时执行一次,确保位置正确
    handleScroll();

    // 清理监听
    return () => window.removeEventListener('scroll', handleScroll);
  }, [navbarHeight]);

  return (
    <header
      ref={navbarRef}
      className="fixed bottom-0 w-full z-50 transition-all duration-500 ease-in-out"
      style={{ transform: navTransform }}
    >
      <nav className="flex items-center justify-between p-4 bg-gray-800 text-white rounded-full mx-auto max-w-4xl">
        {/* Left */}
        <div className="flex-1">
          <Image src="/logo.png" width={35} height={35} alt="Logo" />
        </div>
        {/* Center */}
        <ul className="flex justify-center space-x-6">
          <li className="hover:text-gray-300 cursor-pointer">Home</li>
          <li className="hover:text-gray-300 cursor-pointer">About</li>
          <li className="hover:text-gray-300 cursor-pointer">Projects</li>
        </ul>
        {/* Right */}
        <div className="flex-1 text-right">
          <button className="hover:text-gray-300">Resume</button>
        </div>
      </nav>
    </header>
  );
}

配套的页面结构要求

记得给你的第一屏section加上id="page1",比如首页组件:

// page.tsx(你的首页)
export default function Home() {
  return (
    <main>
      {/* 第一屏,必须加上id="page1" */}
      <section id="page1" className="min-h-screen flex items-center justify-center bg-gray-900">
        <h1 className="text-4xl font-bold">这是第一屏内容</h1>
      </section>
      {/* 第二屏及以后的内容 */}
      <section className="min-h-screen flex items-center justify-center bg-gray-800">
        <h1 className="text-4xl font-bold">这是第二屏内容</h1>
      </section>
    </main>
  );
}

关键细节说明

  • 为什么用transform而不是切换类?:transform属于合成层动画,浏览器会用GPU加速,不会触发重排重绘,不仅更流畅,还能避免定位类型切换导致的跳变。
  • 过渡阶段的计算:当滚动到第一屏剩下的高度刚好等于导航栏高度时,开始让导航栏随滚动向上移动,直到完全进入第二屏后,导航栏刚好停在顶部,整个过程是连续的。
  • 客户端组件标记:Next.js App Router默认是服务器组件,所以必须加上'use client'才能使用useState、useEffect等客户端钩子。
  • 初始化执行handleScroll:避免页面刷新后导航栏位置不正确的问题。

额外优化建议

  • 如果你的第一屏高度不是固定的(比如有动态内容),可以用ResizeObserver监听page1元素的高度变化,实时更新阈值。
  • 可以调整duration-500为你想要的过渡速度,比如duration-300更快,duration-700更慢。
  • 给导航栏加上backdrop-blur-sm类,实现毛玻璃效果,提升视觉层次。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 13:38:02