React使用styled-components实现带左右按钮的导航栏水平滚动
导航栏水平滚动按钮实现方法
两种实现路径都能满足需求,优先推荐无第三方依赖的原生方案,完全匹配你仅使用styled-components的项目要求,不需要额外引入依赖,改造成本极低。
方案1:原生JS + styled-components实现(推荐)
不需要安装任何额外包,直接基于现有代码改造即可,核心逻辑是通过DOM原生的scrollBy方法控制菜单区域滚动,监听滚动位置判断按钮禁用状态。
- 首先调整导航的DOM结构,拆分为外层容器、左右控制按钮、可滚动菜单区三个部分,所有样式用styled-components编写:
import styled from 'styled-components'; import { useState, useRef, useEffect } from 'react'; // 导航最外层容器 const NavWrapper = styled.div` display: flex; align-items: center; width: 100%; position: relative; background: #fff; border-bottom: 1px solid #eee; `; // 通用滚动按钮样式 const ScrollBtn = styled.button` flex-shrink: 0; width: 36px; height: 36px; border: none; border-radius: 50%; background: #f5f5f5; cursor: pointer; display: flex; align-items: center; justify-content: center; z-index: 10; &:disabled { opacity: 0.3; cursor: not-allowed; } `; // 可滚动菜单区域 const NavMenuList = styled.div` flex: 1; display: flex; gap: 24px; overflow-x: auto; scroll-behavior: smooth; /* 隐藏默认滚动条 */ &::-webkit-scrollbar { display: none; } -ms-overflow-style: none; scrollbar-width: none; padding: 0 12px; `; // 原有导航选项卡,加不收缩属性即可 const NavTab = styled.div` flex-shrink: 0; padding: 12px 0; cursor: pointer; /* 保留你原来写的选中态、hover等样式,不需要改动 */ `;
- 补充滚动交互逻辑,直接在组件里写即可:
const Navigation = () => { const menuRef = useRef(null); // 控制左右按钮的禁用状态 const [isLeftDisabled, setIsLeftDisabled] = useState(true); const [isRightDisabled, setIsRightDisabled] = useState(false); // 实时更新按钮状态:滚到最左禁用左按钮,滚到最右禁用右按钮 const updateBtnStatus = () => { const menu = menuRef.current; if (!menu) return; setIsLeftDisabled(menu.scrollLeft === 0); setIsRightDisabled(menu.scrollLeft + menu.clientWidth >= menu.scrollWidth - 1); }; useEffect(() => { const menu = menuRef.current; if (!menu) return; updateBtnStatus(); menu.addEventListener('scroll', updateBtnStatus); window.addEventListener('resize', updateBtnStatus); return () => { menu.removeEventListener('scroll', updateBtnStatus); window.removeEventListener('resize', updateBtnStatus); }; }, []); // 点击按钮滚动固定距离,数值可以根据需求调整 const handleScroll = (direction) => { const menu = menuRef.current; if (!menu) return; const scrollDistance = 200; menu.scrollBy({ left: direction === 'left' ? -scrollDistance : scrollDistance, behavior: 'smooth' }); }; return ( <NavWrapper> <ScrollBtn onClick={() => handleScroll('left')} disabled={isLeftDisabled} > {/* 替换成你自己的左箭头图标即可 */} {'<'} </ScrollBtn> <NavMenuList ref={menuRef}> {/* 这里直接放你原来写的路由选项卡遍历逻辑,不需要改动 */} {tabs.map(tab => <NavTab key={tab.path}>{tab.name}</NavTab>)} </NavMenuList> <ScrollBtn onClick={() => handleScroll('right')} disabled={isRightDisabled} > {/* 替换成你自己的右箭头图标即可 */} {'>'} </ScrollBtn> </NavWrapper> ) };
你的原有路由跳转、选项卡选中逻辑完全不需要改动,套上外层结构就能用。
方案2:使用react-horizontal-scrolling-menu实现
如果不想自己写边界判断逻辑,可以用这个包实现,样式依然全部用styled-components控制:
- 先安装依赖:
npm i react-horizontal-scrolling-menu - 封装组件,覆盖包的默认样式即可:
import { ScrollMenu, VisibilityContext } from 'react-horizontal-scrolling-menu'; import styled from 'styled-components'; import { useContext } from 'react'; // 样式和方案1保持一致即可 const LeftBtn = styled.button`/* 左按钮样式 */`; const RightBtn = styled.button`/* 右按钮样式 */`; const MenuWrapper = styled.div`/* 导航外层样式 */`; const TabItem = styled.div`/* 选项卡原有样式 */`; // 自定义左箭头 const LeftArrow = () => { const { scrollPrev, isFirstItemVisible } = useContext(VisibilityContext); return ( <LeftBtn onClick={() => scrollPrev()} disabled={isFirstItemVisible}> {'<'} </LeftBtn> ) }; // 自定义右箭头 const RightArrow = () => { const { scrollNext, isLastItemVisible } = useContext(VisibilityContext); return ( <RightBtn onClick={() => scrollNext()} disabled={isLastItemVisible}> {'>'} </RightBtn> ) }; const Navigation = () => { return ( <MenuWrapper> <ScrollMenu LeftArrow={LeftArrow} RightArrow={RightArrow}> {tabs.map(tab => <TabItem key={tab.path} itemId={tab.path}>{tab.name}</TabItem>)} </ScrollMenu> </MenuWrapper> ) };
小提示:如果你的导航选项卡是接口异步返回的,等数据渲染完成后记得手动触发一次按钮状态更新,避免边界判断不准。
内容的提问来源于stack exchange,提问作者anas shah
相关产品推荐
相关产品推荐

