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

如何在React中让Tailwind滚动吸附与平滑滚动共存?

解决React中Tailwind滚动吸附与平滑滚动冲突问题

问题描述

我正在开发一个React单页应用,想同时实现Tailwind的滚动吸附和平滑滚动功能,但每次开启y轴滚动吸附后,平滑滚动就失效了。我是React新手,求帮忙解决。

现有代码

App.jsx

import React from "react";
import Header from "./components/Header"
import First from "./components/First";
import Second from "./components/Second";
import Third from "./components/Third";

function App() {
  return (
    <div className="overflow-y-auto snap-y snap-mandatory h-[100vh] w-[100%]">
      <Header />
      <First />
      <Second />
      <Third />
    </div>
  );
}

export default App;

First组件

import React from "react";

const First = () => {
  return (
    <div
      className="w-full h-screen flex justify-center items-center bg-blue-400 snap-center"
      link="first"
    >
      First
    </div>
  );
};

export default First;

Header组件

import React, { useState } from "react";
import { FaBars, FaTimes } from "react-icons/fa";
import { Link } from "react-scroll";

const Header = () => {
  const [nav, setNav] = useState(false);

  const links = [
    {
      id: 1,
      link: "first",
    },
    {
      id: 2,
      link: "second",
    },
    {
      id: 3,
      link: "third",
    },
  ];

  return (
    <div className="flex justify-between items-center w-full h-20 px-4 text-white bg-black fixed">
      <div>
        <h1 className="text-5xl font-signature ml-2">Practice Youtuber Tutorial</h1>
      </div>

      <ul className="hidden md:flex">
        {links.map(({ id, link }) => (
          <li
            key={id}
            className="px-4 cursor-pointer capitalize font-medium text-gray-500 hover:scale-105 duration-200"
          >
            <Link to={link} smooth duration={500}>
              {link}
            </Link>
          </li>
        ))}
      </ul>

      <div
        onClick={() => setNav(!nav)}
        className="cursor-pointer pr-4 z-10 text-gray-500 md:hidden"
      >
        {nav ? <FaTimes size={30} /> : <FaBars size={30} />}
      </div>

      {nav && (
        <ul className="flex flex-col justify-center items-center absolute top-0 left-0 w-full h-screen bg-gradient-to-b from-black to-gray-800 text-gray-500">
          {links.map(({ id, link }) => (
            <li
              key={id}
              className="px-4 cursor-pointer capitalize py-6 text-4xl"
            >
              <Link
                onClick={() => setNav(!nav)}
                to={link}
                smooth
                duration={500}
              >
                {link}
              </Link>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

export default Header;

解决方案

问题根源是scroll-snap的优先级高于平滑滚动动画,会中断react-scroll的平滑效果。我们可以通过临时禁用scroll-snap、完成平滑滚动后再恢复的方式解决:

步骤1:修改App组件,添加自定义滚动逻辑

import React, { useRef } from "react";
import Header from "./components/Header"
import First from "./components/First";
import Second from "./components/Second";
import Third from "./components/Third";

function App() {
  const scrollContainerRef = useRef(null);

  // 自定义滚动方法,处理平滑滚动与scroll-snap的兼容
  const scrollToSection = (targetId, duration = 500) => {
    const target = document.querySelector(`[link="${targetId}"]`);
    if (!target || !scrollContainerRef.current) return;

    // 临时移除scroll-snap类,避免中断平滑滚动
    scrollContainerRef.current.classList.remove("snap-y", "snap-mandatory");

    // 原生平滑滚动到目标区块
    target.scrollIntoView({
      behavior: "smooth",
      block: "start"
    });

    // 滚动动画完成后恢复scroll-snap,保留手动滚动的吸附效果
    setTimeout(() => {
      scrollContainerRef.current.classList.add("snap-y", "snap-mandatory");
    }, duration);
  };

  return (
    <div 
      ref={scrollContainerRef}
      className="overflow-y-auto snap-y snap-mandatory h-[100vh] w-[100%]"
    >
      {/* 将自定义滚动方法传给Header组件 */}
      <Header scrollToSection={scrollToSection} />
      <First />
      <Second />
      <Third />
    </div>
  );
}

export default App;
import React, { useState } from "react";
import { FaBars, FaTimes } from "react-icons/fa";

const Header = ({ scrollToSection }) => {
  const [nav, setNav] = useState(false);

  const links = [
    {
      id: 1,
      link: "first",
    },
    {
      id: 2,
      link: "second",
    },
    {
      id: 3,
      link: "third",
    },
  ];

  return (
    <div className="flex justify-between items-center w-full h-20 px-4 text-white bg-black fixed">
      <div>
        <h1 className="text-5xl font-signature ml-2">Practice Youtuber Tutorial</h1>
      </div>

      <ul className="hidden md:flex">
        {links.map(({ id, link }) => (
          <li
            key={id}
            className="px-4 cursor-pointer capitalize font-medium text-gray-500 hover:scale-105 duration-200"
            onClick={() => scrollToSection(link, 500)}
          >
            {link}
          </li>
        ))}
      </ul>

      <div
        onClick={() => setNav(!nav)}
        className="cursor-pointer pr-4 z-10 text-gray-500 md:hidden"
      >
        {nav ? <FaTimes size={30} /> : <FaBars size={30} />}
      </div>

      {nav && (
        <ul className="flex flex-col justify-center items-center absolute top-0 left-0 w-full h-screen bg-gradient-to-b from-black to-gray-800 text-gray-500">
          {links.map(({ id, link }) => (
            <li
              key={id}
              className="px-4 cursor-pointer capitalize py-6 text-4xl"
              onClick={() => {
                scrollToSection(link, 500);
                setNav(!nav);
              }}
            >
              {link}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

export default Header;

说明

  • 点击导航时先移除滚动容器的snap-y和snap-mandatory类,让平滑滚动不受干扰
  • 使用原生scrollIntoView实现平滑滚动,动画完成后通过setTimeout恢复scroll-snap类,确保手动滚动时依然有吸附效果
  • 放弃react-scroll的Link组件,因为其内置的smooth逻辑与scroll-snap存在兼容性问题,自定义滚动逻辑更可控

内容的提问来源于stack exchange,提问作者React 0o0

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 03:10:15