点击按钮实现Flexbox项左右滑动过渡的组件开发咨询
问题描述
我想开发一个类似NBA官网顶部的日程展示组件,或是NFL官网上可切换不同周的导航组件——点击侧边按钮时,flex容器内的项能向左或向右滑动。
我已经用日期做了示例,样式部分符合需求,但不知道怎么实现点击按钮调整flexbox项滑动的功能,也没找到同类组件的参考方案。
当前代码如下:
App.tsx
import "./App.css"; function getDaysInMonth(month: number, year: number) { const date = new Date(year, month - 1, 1); const days = []; while (date.getMonth() === month - 1) { days.push(new Date(date)); date.setDate(date.getDate() + 1); } return days; } function App() { const dates = getDaysInMonth(12, 2023); console.log(dates[0]); return ( <div className="p-8"> <div className="w-full border text-center bg-zinc-200 mb-2"> <h1>App</h1> </div> <div className="flex gap-2"> <button className="border w-24 hover:bg-zinc-200">L</button> <div className="mx-8 border max-w-screen h-12 flex gap-4 items-center justify-start overflow-x-auto"> {dates.map((date) => { date.setHours(0, 0, 0, 0); const month = date.toLocaleString("default", { month: "short" }); const day = date.getDate(); console.log(month + day); return ( <div key={day} className="border text-sm flex p-2" onClick={() => console.log("clicked " + month + day)} > {month} <span className="ml-1">{day}</span> </div> ); })} </div> <button className="border w-24 hover:bg-zinc-200">R</button> </div> </div> ); } export default App;
App.css
@tailwind base; @tailwind components; @tailwind utilities;
解决方案
要实现按钮控制flex容器的横向滑动,核心是利用DOM元素的scrollBy方法,结合React的useRef获取容器引用,具体实现步骤如下:
- 引入useRef:获取flex容器的DOM节点,用于直接操作滚动行为。
- 添加滚动逻辑:给左右按钮绑定点击事件,调用
scrollBy方法控制滚动距离(正数向右滚动,负数向左滚动)。 - 可选优化:如果需要精准滚动(比如每次滚动固定数量的日期项),可以获取单个日期项的宽度,计算滚动步长。
修改后的完整代码:
import "./App.css"; import { useRef } from "react"; function getDaysInMonth(month: number, year: number) { const date = new Date(year, month - 1, 1); const days = []; while (date.getMonth() === month - 1) { days.push(new Date(date)); date.setDate(date.getDate() + 1); } return days; } function App() { const dates = getDaysInMonth(12, 2023); const scrollContainerRef = useRef<HTMLDivElement>(null); // 每次滚动的步长,可根据需求调整 const scrollStep = 200; const handleScrollLeft = () => { scrollContainerRef.current?.scrollBy({ left: -scrollStep, behavior: "smooth" // 开启平滑滚动,可选 }); }; const handleScrollRight = () => { scrollContainerRef.current?.scrollBy({ left: scrollStep, behavior: "smooth" }); }; return ( <div className="p-8"> <div className="w-full border text-center bg-zinc-200 mb-2"> <h1>App</h1> </div> <div className="flex gap-2"> <button className="border w-24 hover:bg-zinc-200" onClick={handleScrollLeft} > L </button> <div ref={scrollContainerRef} className="mx-8 border max-w-screen h-12 flex gap-4 items-center justify-start overflow-x-auto" > {dates.map((date) => { date.setHours(0, 0, 0, 0); const month = date.toLocaleString("default", { month: "short" }); const day = date.getDate(); return ( <div key={date.getTime()} // 用getTime()避免日期重复的key冲突 className="border text-sm flex p-2 min-w-[60px] justify-center" // 设置最小宽度保证布局一致 onClick={() => console.log("clicked " + month + day)} > {month} <span className="ml-1">{day}</span> </div> ); })} </div> <button className="border w-24 hover:bg-zinc-200" onClick={handleScrollRight} > R </button> </div> </div> ); } export default App;
关键说明:
- useRef的使用:
scrollContainerRef绑定到flex容器,确保能直接访问其DOM属性和方法。 - scrollBy方法:
scrollBy({ left: 数值, behavior: "smooth" })控制滚动,behavior: "smooth"实现平滑滚动效果,也可以设置为"auto"立即滚动。 - key优化:将原来的
key={day}改为key={date.getTime()},避免当月有重复日期(虽然不会出现,但更严谨)。 - 日期项宽度:给日期项添加
min-w-[60px],保证每个项宽度一致,滚动步长的计算更精准。
如果想要实现精准滚动N个日期项,可以在组件挂载后获取单个日期项的宽度,动态计算滚动步长:
import { useRef, useEffect, useState } from "react"; // ...其他代码 function App() { const dates = getDaysInMonth(12, 2023); const scrollContainerRef = useRef<HTMLDivElement>(null); const [itemWidth, setItemWidth] = useState<number>(0); const itemsToScroll = 3; // 每次滚动3个项 useEffect(() => { // 获取第一个日期项的宽度 const firstItem = scrollContainerRef.current?.querySelector("div"); if (firstItem) { setItemWidth(firstItem.offsetWidth + 16); // 加上gap的4*4=16px } }, []); const handleScrollLeft = () => { scrollContainerRef.current?.scrollBy({ left: -itemWidth * itemsToScroll, behavior: "smooth" }); }; const handleScrollRight = () => { scrollContainerRef.current?.scrollBy({ left: itemWidth * itemsToScroll, behavior: "smooth" }); }; // ...返回JSX部分不变 }
内容的提问来源于stack exchange,提问作者Simon1
相关产品推荐
相关产品推荐

