如何在React中使用CSS的translateX使活动元素居中?或者有其他方法吗?

huangapple go评论66阅读模式
英文:

how to make active element center in react js using css traslateX? or any other way?

问题

我需要帮助!我不是CSS的专家,所以我为我的情况做了一些事情,但这并没有完全帮助。在这里,我尝试解释我的问题。

有人可以帮忙吗?

Live Code Demo

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>
  );
}

如何在React中使用CSS的translateX使活动元素居中?或者有其他方法吗?

活动圆圈需要始终位于窗口的水平中心位置。其余的圆圈需要根据活动圆圈自动对齐。

如何做到这一点?我希望您能理解。有人可以帮助我吗?

我在这里尝试解释。

在上面的代码中,活动点圆将位于中心。所以您需要做的是:

  1. 获取窗口的总宽度。
  2. 获取总宽度位置的中心。
  3. 获取活动点的当前位置。

示意图:

|-------------------|-------------------|
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?

Live Code Demo

import &quot;./styles.css&quot;;
import React, { useRef, useState, useEffect } from &quot;react&quot;;
import styles from &quot;styled-components&quot;;
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 }) =&gt; (isActive ? &quot;#fc916a&quot; : &quot;#436073&quot;)};
transition: transform 0.3s, background-color 0.3s;
transform: ${({ isActive, index, activeIndex, centerIndex }) =&gt; {
const distanceFromCenter = index - centerIndex;
const distanceFromActive = index - activeIndex;
if (isActive) {
return &quot;scale(1.3)&quot;;
} else if (distanceFromActive === 1) {
return &quot;translateX(25px)&quot;;
} else if (distanceFromActive === -1) {
return &quot;translateX(-25px)&quot;;
} else if (distanceFromCenter &gt; 0) {
return &quot;translateX(50px)&quot;;
} else if (distanceFromCenter &lt; 0) {
return &quot;translateX(-50px)&quot;;
} else {
return &quot;scale(1)&quot;;
}
}};
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: &quot;01-01-2002&quot; },
{ info_date: &quot;01-01-2003&quot; },
{ info_date: &quot;01-01-2004&quot; },
{ info_date: &quot;01-01-2005&quot; },
{ info_date: &quot;01-01-2006&quot; },
{ info_date: &quot;01-01-2007&quot; }
]
};
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(() =&gt; {
const activeDotPosition = activeDot.current.getBoundingClientRect();
const activePosition =
activeDotPosition.x + activeDotPosition.width / 2 - centerPosition;
setActivePosition(activePosition);
}, [windowWidth]);
const result = centerPosition - activePosition;
const translateX = result &gt; 0 ? `${result}px` : `-${Math.abs(result)}px`;
return (
&lt;React.Fragment&gt;
&lt;div
className=&quot;line&quot;
style={{
position: &quot;relative&quot;,
width: &quot;100%&quot;,
overflowX: &quot;hidden&quot;,
height: &quot;350px&quot;
}}
&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
windowWidth = {windowWidth}
&lt;br /&gt;
centerPosition = {centerPosition}
&lt;br /&gt;
activePosition = {activePosition}
&lt;br /&gt;
result= {result}
&lt;br /&gt;
{translateX}
&lt;br /&gt;
&lt;DotsWrapper style={{ transform: `translateX(${translateX})` }}&gt;
{mobileContent.page_blocks.map((item, index) =&gt;
index === activeIndex ? (
&lt;CenterDot
key={index}
isActive
index={index}
centerIndex={centerIndex}
activeIndex={activeIndex}
ref={activeDot}
&gt;
{new Date(item.info_date).getFullYear()}
&lt;/CenterDot&gt;
) : (
&lt;Dot
key={index}
isActive={index === activeIndex}
index={index}
centerIndex={centerIndex}
activeIndex={activeIndex}
onClick={() =&gt; setActiveIndex(index)}
&gt;
{new Date(item.info_date).getFullYear()}
&lt;/Dot&gt;
)
)}
&lt;/DotsWrapper&gt;
&lt;/div&gt;
&lt;/React.Fragment&gt;
);
}

如何在React中使用CSS的translateX使活动元素居中?或者有其他方法吗?

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

  1. get the total width of widows.
  2. get the center of the total width position.
  3. 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 () =&gt; {
clearTimeout(timer);
timer = setTimeout(() =&gt; {
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(() =&gt; {
/* 
I&#39;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&#39;t have to worry about resizing
*/
const debouncedHandleResize = debounce(function handleResize() {
setViewportCenter(Math.floor(window.innerWidth / 2));
}, 250);
window.addEventListener(&quot;resize&quot;, debouncedHandleResize);
return () =&gt; {
window.removeEventListener(&quot;resize&quot;, debouncedHandleResize);
};
}, []);
React.useEffect(() =&gt; {
const wrapperOffset = wrapperElement.current.parentNode.offsetLeft;
const element = wrapperElement.current.childNodes[isActive];
const offset = -element.offsetLeft - wrapperOffset - element.clientWidth / 2;
wrapperElement.current.style.setProperty(
&quot;--xPos&quot;,
viewPortCenter + offset + &quot;px&quot;
);
}, [isActive, viewPortCenter]);
return (
&lt;ul className=&quot;dots-wrapper&quot; ref={wrapperElement}&gt;
{elements.map((el, i) =&gt; (
&lt;li
key={el}
onClick={() =&gt; setIsActive(i)}
className={isActive === i ? &quot;dot active&quot; : &quot;dot&quot;}
&gt;
{el}
&lt;/li&gt;
))}
&lt;/ul&gt;
);
}
const rootElement = document.getElementById(&quot;root&quot;);
const root = ReactDOM.createRoot(rootElement);
root.render(
&lt;Slider /&gt;
)

<!-- 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 -->

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js&quot;&gt;&lt;/script&gt;
&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年3月4日 02:27:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/75630652.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定