如何在React中用CSS translateX或其他方式实现激活元素居中?
问题:激活状态圆点始终保持窗口水平居中的实现方案
需求说明
激活状态的圆点始终保持在窗口水平中心位置,其余圆点根据激活圆点自动对齐。
当前代码
import "./styles.css"; import React, { useRef, useState, useEffect } from "react"; import styles from "styled-components"; const DotsWrapper = styles.div` display: flex; justify-content: center; top: 0; left: 0; right: 0; bottom: 0; `; const Dot = styles.div` width: 50px; height: 50px; text-align: center; padding: 2rem; border-radius: 50%; background-color: ${({ isActive }) => (isActive ? "#fc916a" : "#436073")}; transition: transform 0.3s, background-color 0.3s; transform: ${({ isActive, index, activeIndex, centerIndex }) => { const distanceFromCenter = index - centerIndex; const distanceFromActive = index - activeIndex; if (isActive) { return "scale(1.3)"; } else if (distanceFromActive === 1) { return "translateX(25px)"; } else if (distanceFromActive === -1) { return "translateX(-25px)"; } else if (distanceFromCenter > 0) { return "translateX(50px)"; } else if (distanceFromCenter < 0) { return "translateX(-50px)"; } else { return "scale(1)"; } }}; display: flex; justify-content: center; align-items: center; color: #fff; font-size: 20px; margin: 0 10px; `; const CenterDot = styles(Dot)` transform: scale(1.3); `; export default function App() { const mobileContent = { page_blocks: [ { info_date: "01-01-2002" }, { info_date: "01-01-2003" }, { info_date: "01-01-2004" }, { info_date: "01-01-2005" }, { info_date: "01-01-2006" }, { info_date: "01-01-2007" } ] }; const [activeIndex, setActiveIndex] = React.useState(0); const activeDot = React.useRef(null); const centerIndex = Math.floor(mobileContent.page_blocks.length / 2); const windowWidth = window.innerWidth; const centerPosition = windowWidth / 2; const [activePosition, setActivePosition] = React.useState(0); React.useEffect(() => { const activeDotPosition = activeDot.current.getBoundingClientRect(); const activePosition = activeDotPosition.x + activeDotPosition.width / 2 - centerPosition; setActivePosition(activePosition); }, [windowWidth]); const result = centerPosition - activePosition; const translateX = result > 0 ? `${result}px` : `-${Math.abs(result)}px`; return ( <React.Fragment> <div className="line" style={{ position: "relative", width: "100%", overflowX: "hidden", height: "350px" }} > <br /> <br /> <br /> windowWidth = {windowWidth} <br /> centerPosition = {centerPosition} <br /> activePosition = {activePosition} <br /> result= {result} <br /> {translateX} <br /> <DotsWrapper style={{ transform: `translateX(${translateX})` }}> {mobileContent.page_blocks.map((item, index) => index === activeIndex ? ( <CenterDot key={index} isActive index={index} centerIndex={centerIndex} activeIndex={activeIndex} ref={activeDot} > {new Date(item.info_date).getFullYear()} </CenterDot> ) : ( <Dot key={index} isActive={index === activeIndex} index={index} centerIndex={centerIndex} activeIndex={activeIndex} onClick={() => setActiveIndex(index)} > {new Date(item.info_date).getFullYear()} </Dot> ) )} </DotsWrapper> </div> </React.Fragment> ); }
当前效果

尝试思路
- 获取窗口总宽度
- 获取窗口中心位置
- 获取激活圆点当前位置
- 通过计算移动距离,使用CSS
translateX实现激活圆点居中,但未达到预期效果
解决方案
修改要点
- 简化圆点样式:移除每个圆点自身复杂的偏移逻辑,仅保留激活状态的缩放效果,避免与容器的移动逻辑冲突。
- 监听窗口 resize:实时更新窗口宽度,保证窗口大小变化时居中逻辑依然有效。
- 修正居中计算逻辑:当激活索引切换或窗口大小变化时,重新计算容器需要移动的距离,确保激活圆点始终处于窗口中心。
修改后的完整代码
import "./styles.css"; import React, { useRef, useState, useEffect } from "react"; import styles from "styled-components"; const DotsWrapper = styles.div` display: flex; position: relative; transition: transform 0.3s ease; /* 添加上过渡动画 */ `; const Dot = styles.div` width: 50px; height: 50px; text-align: center; padding: 2rem; border-radius: 50%; background-color: ${({ isActive }) => (isActive ? "#fc916a" : "#436073")}; transition: transform 0.3s, background-color 0.3s; transform: ${({ isActive }) => isActive ? "scale(1.3)" : "scale(1)"}; display: flex; justify-content: center; align-items: center; color: #fff; font-size: 20px; margin: 0 10px; flex-shrink: 0; /* 防止圆点被压缩变形 */ cursor: pointer; `; export default function App() { const mobileContent = { page_blocks: [ { info_date: "01-01-2002" }, { info_date: "01-01-2003" }, { info_date: "01-01-2004" }, { info_date: "01-01-2005" }, { info_date: "01-01-2006" }, { info_date: "01-01-2007" } ] }; const [activeIndex, setActiveIndex] = useState(0); const [windowWidth, setWindowWidth] = useState(window.innerWidth); const [wrapperTranslateX, setWrapperTranslateX] = useState(0); const activeDot = useRef(null); // 监听窗口大小变化 useEffect(() => { const handleResize = () => setWindowWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); // 计算容器偏移量,保证激活圆点居中 useEffect(() => { if (!activeDot.current) return; const activeDotRect = activeDot.current.getBoundingClientRect(); const activeCenter = activeDotRect.left + activeDotRect.width / 2; const windowCenter = windowWidth / 2; // 容器需要移动的距离:让激活圆点的中心对齐窗口中心 const translateX = -(activeCenter - windowCenter); setWrapperTranslateX(translateX); }, [activeIndex, windowWidth]); return ( <React.Fragment> <div className="line" style={{ position: "relative", width: "100%", overflowX: "hidden", height: "350px", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }} > <DotsWrapper style={{ transform: `translateX(${wrapperTranslateX}px)` }}> {mobileContent.page_blocks.map((item, index) => ( <Dot key={index} isActive={index === activeIndex} ref={index === activeIndex ? activeDot : null} onClick={() => setActiveIndex(index)} > {new Date(item.info_date).getFullYear()} </Dot> ))} </DotsWrapper> </div> </React.Fragment> ); }
逻辑说明
- 窗口监听:通过
resize事件实时更新窗口宽度,确保窗口大小变化时居中逻辑自动调整。 - 偏移计算:获取激活圆点的中心位置,计算其与窗口中心的差值,通过容器的
translateX反向偏移,让激活圆点始终处于窗口中心。 - 样式优化:添加
flex-shrink:0防止圆点被压缩,给容器添加上过渡动画,让切换过程更平滑。
内容的提问来源于stack exchange,提问作者Jackson Kasi
相关产品推荐
相关产品推荐

