英文:
how to make active element center in react js using css traslateX? or any other way?
问题
我需要帮助!我不是CSS的专家,所以我为我的情况做了一些事情,但这并没有完全帮助。在这里,我尝试解释我的问题。
有人可以帮忙吗?
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>
);
}
活动圆圈需要始终位于窗口的水平中心位置。其余的圆圈需要根据活动圆圈自动对齐。
如何做到这一点?我希望您能理解。有人可以帮助我吗?
我在这里尝试解释。
在上面的代码中,活动点圆将位于中心。所以您需要做的是:
- 获取窗口的总宽度。
- 获取总宽度位置的中心。
- 获取活动点的当前位置。
示意图:
|-------------------|-------------------|
1000px (总宽度)
----活动点-------------------------
250px
所以,
我们需要测量,需要移动活动点多少空间或像素,使其位于中心位置?
(总宽度) - (中心位置) - (活动点位置)
例如 1:
result = 1000px - 500px - 500px = 250px
例如 2:
result = 1000px - 500px - 800px = -350px
所以现在
(活动点位置)+(result)
例如 1:
250px + 250px = 500px
例如 2:
800px +(-300px)= 500px
因此,使用此计算使活动点位置居中(使用CSS的translateX或其他方式),而其他非活动点将自动对齐。
英文:
I need one help! i am not a master in css. so i did something for my case. but this does not help full. here i try to explain my problem.
any one can help?
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>
);
}
the active circle needs to be always the center of windows horizontally.
and the remaining circles need to be auto align based on the active circle.
how to do this? i hope u will understand. anyone can please help me?
i try to explain here.
in this code above, the active dot circle will be in the center.
so what u need to do is,
current
- get the total width of widows.
- get the center of the total width position.
- get the current position of active dot.
ex diagram.
|-------------------|-------------------|
1000px ( total width )
----active-dot-------------------------
250px
so,
we need to measure. how much space or px u need to move active dot for making it center?
(total width ) - ( center position ) - (active dot position )
ex 1:
result = 1000px - 500px - 500px = 250px
ex 2:
result = 1000px - 500px - 800px = -350px
so now
(active dot position ) + ( result )
ex 1:
250px + 250px = 500px
ex 2:
800px + ( - 300px ) = 500px
so using this calculation make an active dot position center ( use css translateX or any other )
and other not active dots will auto align
答案1
得分: 1
我认为可能有更简单的方法来考虑这个问题。您实际上需要知道的是视口的中心(或父容器的中心,如果您希望它按照这种方式工作,我在下面的示例中使用了视口),活动元素与其父容器的偏移,以及活动点的大小。有了这些信息,您可以将父容器的左属性设置为活动点的左侧减去活动点宽度的一半,以使其居中。
/* 处理调整大小事件的去抖函数 */
function debounce(cb, ms) {
let timer;
return () => {
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
cb.apply(this, arguments);
}, ms);
};
}
function Slider({
elements = [2000, 2001, 2002, 2003, 2004, 2005]
}) {
/* 计算视口中心的初始值 */
const initialCenter = Math.floor(window.innerWidth / 2);
const [viewPortCenter, setViewportCenter] = React.useState(initialCenter);
const [isActive, setIsActive] = React.useState(0);
const wrapperElement = React.useRef(null);
React.useEffect(() => {
/* 在这里我使用了视口,但您可以传递一个包含元素的引用并获取其clientWidth,
除非您的包含元素是固定大小的,否则您显然不必担心调整大小 */
const debouncedHandleResize = debounce(function handleResize() {
setViewportCenter(Math.floor(window.innerWidth / 2));
}, 250);
window.addEventListener("resize", debouncedHandleResize);
return () => {
window.removeEventListener("resize", debouncedHandleResize);
};
}, []);
React.useEffect(() => {
const wrapperOffset = wrapperElement.current.parentNode.offsetLeft;
const element = wrapperElement.current.childNodes[isActive];
const offset = -element.offsetLeft - wrapperOffset - element.clientWidth / 2;
wrapperElement.current.style.setProperty(
"--xPos",
viewPortCenter + offset + "px"
);
}, [isActive, viewPortCenter]);
return (
<ul className="dots-wrapper" ref={wrapperElement}>
{elements.map((el, i) => (
<li
key={el}
onClick={() => setIsActive(i)}
className={isActive === i ? "dot active" : "dot"}
>
{el}
</li>
))}
</ul>
);
}
const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);
root.render(
<Slider />
);
body {
height: 100vh;
/* 渐变显示中央标记 */
background-image: linear-gradient(
90deg,
rgb(248, 248, 228) 50%,
rgb(250, 243, 207) 50%
);
margin: 1rem;
}
.dots-wrapper {
--size: 3rem;
--scale: 1.3;
--xPos: calc(50% - calc(var(--size) * var(--scale) / 2));
align-items: center;
display: flex;
gap: 0.5rem;
left: var(--xPos);
list-style-type: none;
margin: 0;
padding: 0;
position: relative;
transition: left 0.25s ease-in-out, background-color 0.25s ease-in-out;
}
.dot {
aspect-ratio: 1;
background-color: #436073;
border-radius: 50%;
color: white;
cursor: pointer;
display: grid;
flex-shrink: 0;
font-family: sans-serif;
place-content: center;
width: var(--size);
user-select: none;
}
.dot.active {
background-color: #fc916a;
width: calc(var(--size) * var(--scale));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
英文:
I think there might be an easier way to think of this. Really what you need to know is the center of the viewport (or the center of a parent containing element if that's how you want it to work, I used the viewport in the snippet below), the offset of the active element from it's parent container, and the size of the active dot. With this you can set the left property of the parent container to the left side of the active dot less the half the width of the active dot so it is centered.
<!-- begin snippet: js hide: false console: true babel: true -->
<!-- language: lang-js -->
/*
Debounce for handling resize events
*/
function debounce(cb, ms) {
let timer;
return () => {
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
cb.apply(this, arguments);
}, ms);
};
}
function Slider({
elements = [2000, 2001, 2002, 2003, 2004, 2005]
}) {
/* Calculate the initial value for the center of the viewport */
const initialCenter = Math.floor(window.innerWidth / 2);
const [viewPortCenter, setViewportCenter] = React.useState(initialCenter);
const [isActive, setIsActive] = React.useState(0);
const wrapperElement = React.useRef(null);
React.useEffect(() => {
/*
I'm using the viewport here but you could pass a ref
from a containing element and get its clientWidth
unless your containing element is a fixed size then
you obviously don't have to worry about resizing
*/
const debouncedHandleResize = debounce(function handleResize() {
setViewportCenter(Math.floor(window.innerWidth / 2));
}, 250);
window.addEventListener("resize", debouncedHandleResize);
return () => {
window.removeEventListener("resize", debouncedHandleResize);
};
}, []);
React.useEffect(() => {
const wrapperOffset = wrapperElement.current.parentNode.offsetLeft;
const element = wrapperElement.current.childNodes[isActive];
const offset = -element.offsetLeft - wrapperOffset - element.clientWidth / 2;
wrapperElement.current.style.setProperty(
"--xPos",
viewPortCenter + offset + "px"
);
}, [isActive, viewPortCenter]);
return (
<ul className="dots-wrapper" ref={wrapperElement}>
{elements.map((el, i) => (
<li
key={el}
onClick={() => setIsActive(i)}
className={isActive === i ? "dot active" : "dot"}
>
{el}
</li>
))}
</ul>
);
}
const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);
root.render(
<Slider />
)
<!-- language: lang-css -->
body {
height: 100vh;
/* gradient to show the mid mark */
background-image: linear-gradient(
90deg,
rgb(248, 248, 228) 50%,
rgb(250, 243, 207) 50%
);
margin: 1rem;
}
.dots-wrapper {
--size: 3rem;
--scale: 1.3;
--xPos: calc(50% - calc(var(--size) * var(--scale) / 2));
align-items: center;
display: flex;
gap: 0.5rem;
left: var(--xPos);
list-style-type: none;
margin: 0;
padding: 0;
position: relative;
transition: left 0.25s ease-in-out, background-color 0.25s ease-in-out;
}
.dot {
aspect-ratio: 1;
background-color: #436073;
border-radius: 50%;
color: white;
cursor: pointer;
display: grid;
flex-shrink: 0;
font-family: sans-serif;
place-content: center;
width: var(--size);
user-select: none;
}
.dot.active {
background-color: #fc916a;
width: calc(var(--size) * var(--scale));
}
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论