Using window.innerWidth or document.documentElement.clientWidth to center align elements in a viewport

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

Using window.innerWidth or document.documentElement.clientWidth to center align elements in a viewport

问题

我已经为React应用程序创建了一个水平轮播元素。我想要轮播中当前活动的卡片在视口中央居中,以使这个实现对任何设备都能响应。

import React, { useState } from "react";
import styles from "./styles.module.scss";

const data = [
  {
    name: "1"
  },
  {
    name: "2"
  },
  {
    name: "3"
  }
];

const Testimonial: React.FC = (): JSX.Element => {
  const [activeIndex, setActiveIndex] = useState(0);

  const determinePlacement = (): number => {
    const width = 260;
    const startingOffset =
      (Math.max(
        document.documentElement.clientWidth || 0,
        window.innerWidth || 0
      ) -
        width) /
      2;

    if (activeIndex === 0) return startingOffset;

    return -(activeIndex * width) + startingOffset;
  };

  const isActive = (i: number): null | string => {
    return activeIndex === i ? styles.active : null;
  };

  return (
    <>
      <div className={styles.container}>
        <div
          className={styles["card-wrapper"]}
          style={{ transform: `translateX(${determinePlacement()}px)` }}
        >
          {data.map((card, i) => {
            return (
              <div
                onClick={(): void => setActiveIndex(i)}
                className={styles.card}
                key={i}
              >
                {`Card ${i + 1}`}
              </div>
            );
          })}
        </div>
      </div>
      <div className={styles["circles-wrapper"]}>
        {data.map((_, i) => {
          return (
            <div
              key={i}
              onClick={(): void => setActiveIndex(i)}
              className={[styles.circles, isActive(i)].join(" ")}
            />
          );
        })}
      </div>
    </>
  );
};

export default Testimonial;
$cardWidth: 260px;

.circles-wrapper {
    display: flex;
    justify-content: center;
    margin-top: 24px;

    .circles {
        width: 16px;
        height: 16px;
        border-radius: 16px;
        background-color: hsla(207, 73%, 95%, 1);
        filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));
        margin-right: 8px;
        transition: all 0.5s ease;

        &.active {
            background-color: hsla(207, 73%, 57%, 1);
        }
    }
}

.container {
    display: flex;
    position: relative;
    flex-direction: column;
    margin-top: 32px;
    margin-bottom: 32px;

    .card-wrapper {
        display: flex;
        transition: all 1s ease;

        .card {
            display: flex;
            flex-direction: column;
            width: $cardWidth;
            height: 100%;
            background-color: hsla(207, 73%, 95%, 1);
            border-radius: 20px;
            filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));
            padding: 32px;
            margin: 0 10px;
        }
    }
}

我无法确定为什么在我在本地移动设备上运行这个应用程序时间隔会出现错误,元素会偏离得太远。我认为这可能与我不太了解document.documentElement.clientWidthwindow.innerWidth的工作原理,或者与视口宽度有关。是否有人可以帮助我解决这个问题?

编辑:结果,我提供的CodeSandbox示例也不能按预期工作,我可能是在想象事情。

所以问题是:如何可靠地将轮播中的活动卡片居中显示在任何视口中?

英文:

I've created a horizontal carousel element myself for use in a React application. I want the currently active card in the carousel to be centered in the center of the viewport so that this implementation works responsively for any device.

import React, { useState } from &quot;react&quot;;
import styles from &quot;./styles.module.scss&quot;;
const data = [
{
name: &quot;1&quot;
},
{
name: &quot;2&quot;
},
{
name: &quot;3&quot;
}
];
const Testimonial: React.FC = (): JSX.Element =&gt; {
const [activeIndex, setActiveIndex] = useState(0);
const determinePlacement = (): number =&gt; {
const width = 260;
const startingOffset =
(Math.max(
document.documentElement.clientWidth || 0,
window.innerWidth || 0
) -
width) /
2;
if (activeIndex === 0) return startingOffset;
return -(activeIndex * width) + startingOffset;
};
const isActive = (i: number): null | string =&gt; {
return activeIndex === i ? styles.active : null;
};
return (
&lt;&gt;
&lt;div className={styles.container}&gt;
&lt;div
className={styles[&quot;card-wrapper&quot;]}
style={{ transform: `translateX(${determinePlacement()}px)` }}
&gt;
{data.map((card, i) =&gt; {
return (
&lt;div
onClick={(): void =&gt; setActiveIndex(i)}
className={styles.card}
key={i}
&gt;
{`Card ${i + 1}`}
&lt;/div&gt;
);
})}
&lt;/div&gt;
&lt;/div&gt;
&lt;div className={styles[&quot;circles-wrapper&quot;]}&gt;
{data.map((_, i) =&gt; {
return (
&lt;div
key={i}
onClick={(): void =&gt; setActiveIndex(i)}
className={[styles.circles, isActive(i)].join(&quot; &quot;)}
/&gt;
);
})}
&lt;/div&gt;
&lt;/&gt;
);
};
export default Testimonial;

$cardWidth: 260px;
.circles-wrapper {
display: flex;
justify-content: center;
margin-top: 24px;
.circles {
width: 16px;
height: 16px;
border-radius: 16px;
background-color: hsla(207, 73%, 95%, 1);
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));
margin-right: 8px;
transition: all 0.5s ease;
&amp;.active {
background-color: hsla(207, 73%, 57%, 1);
}
}
}
.container {
display: flex;
position: relative;
flex-direction: column;
margin-top: 32px;
margin-bottom: 32px;
.card-wrapper {
display: flex;
transition: all 1s ease;
.card {
display: flex;
flex-direction: column;
width: $cardWidth;
height: 100%;
background-color: hsla(207, 73%, 95%, 1);
border-radius: 20px;
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));
padding: 32px;
margin: 0 10px;
}
}
}

I can't quite figure out why when I re-create this implementation, simplified in a code sandbox (you can view it here https://codesandbox.io/s/currying-wind-krgx7k?file=/src/app.tsx), the elements correctly center in the middle of the viewport but when I run this app locally in dev tools (f12) on mobile devices the spacing is wrong and the elements end up too far to the right?

I'm assuming this has something to do with a lack of understanding on my part about how document.documentElement.clientWidth and window.innerWidth work or something to do with viewport widths. Can anyone enlighten me here please?

Edit: turns out the codesandbox example I provided also doesn't work as expected and I'm imagining things.

So question is: how do you reliably center the active carousel card for any viewport?

答案1

得分: 0

对于那些感兴趣的人,我找到了解决方法,并在下面的codesandbox中包含了一个可行的解决方案。

https://codesandbox.io/s/zen-parm-e5yyn9?file=/src/App.js&amp;resolutionWidth=1024&amp;resolutionHeight=765

最后,我决定尝试计算视口宽度和卡片起始位置之间的间隙是错误的方法。

我使用CSS的flex属性来居中一个包含所有卡片(或盒子,在最新的示例中)的容器,然后在x轴上进行变换。您需要记得考虑元素之间的边距等因素。

这个解决方案更简单、更干净,而且现在可以响应式地工作。

英文:

For those interested, I figured out how to solve this and included a codesandbox below with a working solution.

https://codesandbox.io/s/zen-parm-e5yyn9?file=/src/App.js&amp;resolutionWidth=1024&amp;resolutionHeight=765

In the end, I decided trying to calculate the viewport width and the gap between the start of the viewport and the card was the wrong approach.

I used CSS flex to center a wrapper container for all cards (or boxes, in latest example) which would then be transformed on the x-axis. You need to remember to account for things like margin between elements.

The solution is far simpler, cleaner and works responsively now.

huangapple
  • 本文由 发表于 2023年2月7日 01:36:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75364711.html
匿名

发表评论

匿名网友

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

确定