如何在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;
步骤2:修改Header组件,替换react-scroll的Link
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
相关产品推荐
相关产品推荐

