如何在React中创建具有点的水平滚动。

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

How to make a horizontal scroll with dots using react

问题

以下是您要翻译的内容:

我正在处理一个小部分,我需要水平显示一些 div,所以当它们超出空间时,我使用CSS中的 overflow-x:auto 使其可滚动。

我所做的

<div className="App d-flex">
  {data.map((li) => {
    return (
      <div key={li.id} className="boxes">
        {li.name}
      </div>
    );
  })}
</div>

我的CSS如下

.d-flex {
  display: flex;
}
.boxes {
  padding: 12px;
  margin: 4px;
  background-color: rgb(196, 218, 243);
  min-width: 150px;
  overflow-x: auto;
  text-align: center;
  border-radius: 6px;
}

我想要做的

  • 我想在那里放置点,以便用户单击点时可以滚动。
  • 只有在超过一定数量的项目时,点才应触发,也就是说,如果只有少数项目可以在一个屏幕上显示,那么就没有点,否则会有点。
  • 在这里,我不知道如何使用React Hooks 来实现这一点。

这是我的Code Sandbox

编辑
我试图获得这个

我想为每一页显示点的数量,假设在一页上有四个div,这些可以在一页上显示,不会出现点,但当div达到8个时,它将出现在两页或三页,所以会出现2-3个点,当我点击时,它应该滚动到相应的页面,如图所示。

英文:

I am working on one small part where I have to show some divs horizontally, so when it goes out of the space I am using css as overflow-x:auto so that it becomes scrollable.

What I did

&lt;div className=&quot;App d-flex&quot;&gt;
  {data.map((li) =&gt; {
    return (
      &lt;div key={li.id} className=&quot;boxes&quot;&gt;
        {li.name}
      &lt;/div&gt;
    );
  })}
&lt;/div&gt;

My CSS below

.d-flex {
  display: flex;
}
.boxes {
  padding: 12px;
  margin: 4px;
  background-color: rgb(196, 218, 243);
  min-width: 150px;
  overflow-x: auto;
  text-align: center;
  border-radius: 6px;
}

What I am trying to do

  • I want to put dots there so that when user clicks on dots it should scroll.
  • the dots should trigger only when exceeded a number of items, means if there are less items which can be shown on one screen then no dots or else dots.
  • Here I am not getting the idea how can I do this using react-hooks.

Here is my Code SandBox

Edit
I am trying to get 如何在React中创建具有点的水平滚动。

I want to show number of dots for each page , suppose on one page there are four divs so these can be shown in one page and no dots will be there, but when divs are 8 so it will come in two pages or three so 2-3 dots will come and when i click it should scroll to respective page as shown in picture.

答案1

得分: 2

你需要以下内容:

  • 一个外部容器,用于占据视口的宽度并允许滚动。
  • 一个内部容器,随内容增长并溢出外部容器。

CSS 如下:

.outer-container {
  width: 100%;
  overflow-x: auto;
}

.inner-container {
  width: max-content;
}

使用 Resize Observer API 来监听外部容器或内部容器的大小变化。外部容器在视口大小变化时被调整大小,而内部容器在内容变化时被调整大小。

然后,只有当内部容器大于外部容器时才显示导航菜单。

useEffect(() => {
  const outerContainer = outerContainerRef.current;
  const innerContainer = innerContainerRef.current;
  if (!outerContainer || !innerContainer) return;

  const observer = new ResizeObserver(() => {
    setShowNavigation(innerContainer.clientWidth > outerContainer.clientWidth);
  });

  observer.observe(outerContainer);
  observer observe(innerContainer);

  // 清理
  return () => observer.disconnect();
}, [outerContainerRef.current, innerContainerRef.current]);

在下面的示例中,我使用箭头并向左和向右滚动以简化操作。创建那些点更复杂,因为它涉及计算要显示多少个点并跟踪它们的状态。我相信你可以从中解决问题。

英文:

You'll need:

  • An outer container to stay the width of the viewport and allow scrolling.
  • An inner container to grow with the content and overflow the outer div.

CSS is as follows

.outer-container {
  width: 100%;
  overflow-x: auto;
}

.inner-container {
  width: max-content;
}

Use the Resive Observer API to listen for when either container gets resized. The outer container will be resized when the viewport size changes, while the inner container will be resized when the content changes.

Then just show the navigation menu if the inner container is larger than the outer container.

  useEffect(() =&gt; {
    const outerContainer = outerContainerRef.current;
    const innerContainer = innerContainerRef.current;
    if (!outerContainer || !innerContainer) return;

    const observer = new ResizeObserver(() =&gt; {
      setShowNavigation(innerContainer.clientWidth &gt; outerContainer.clientWidth);
    });

    observer.observe(outerContainer);
    observer.observe(innerContainer);

    // Cleanup
    return () =&gt; observer.disconnect();
  }, [outerContainerRef.current, innerContainerRef.current]);

In the example below, I use arrows and just scroll left and right to simplify. Making those dots is more complex since it involves calculating how many dots to show, and keeping track of their state. I'm sure you can figure it out from there.


Snippet Example

<!-- begin snippet: js hide: true console: true babel: true -->

<!-- language: lang-js -->

let data = [
  {
    name: &#39;steve&#39;,
    id: 1,
  },
  {
    name: &#39;Matt&#39;,
    id: 2,
  },
  {
    name: &#39;michel&#39;,
    id: 3,
  },
  {
    name: &#39;Morgan&#39;,
    id: 4,
  },
  {
    name: &#39;joss&#39;,
    id: 5,
  },
  {
    name: &#39;stephen&#39;,
    id: 6,
  },
  {
    name: &#39;clark&#39;,
    id: 7,
  },
  {
    name: &#39;Simmons&#39;,
    id: 8,
  },
  {
    name: &#39;Chris&#39;,
    id: 9,
  },
  {
    name: &#39;Beven&#39;,
    id: 10,
  },
];

function App() {
  const outerContainerRef = React.createRef();
  const innerContainerRef = React.createRef();
  const [showNavigation, setShowNavigation] = React.useState(false);
  const [num, setNum] = React.useState(3);

  React.useEffect(() =&gt; {
    const outerContainer = outerContainerRef.current;
    const innerContainer = innerContainerRef.current;
    if (!outerContainer || !innerContainer) return;

    const observer = new ResizeObserver(() =&gt; {
      setShowNavigation(innerContainer.clientWidth &gt; outerContainer.clientWidth);
    });

    observer.observe(outerContainer);
    observer.observe(innerContainer);

    // Cleanup
    return () =&gt; observer.disconnect();
  }, [outerContainerRef.current, innerContainerRef.current]);

  function scrollRight() {
    outerContainerRef.current.scrollBy(outerContainerRef.current.clientWidth, 0);
  }

  function scrollLeft() {
    outerContainerRef.current.scrollBy(-outerContainerRef.current.clientWidth, 0);
  }

  function add() {
    setNum((num) =&gt; Math.min(num + 1, data.length));
  }

  function remove() {
    setNum((num) =&gt; Math.max(num - 1, 0));
  }

  return (
    &lt;div&gt;
      &lt;div className=&quot;outer-container d-flex&quot; ref={outerContainerRef}&gt;
        &lt;div className=&quot;inner-container d-flex&quot; ref={innerContainerRef}&gt;
        {data.slice(0, num).map((li) =&gt; {
          return (
            &lt;div key={li.id} className=&quot;boxes&quot;&gt;
              {li.name}
            &lt;/div&gt;
          );
        })}
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div className=&quot;arrow-container&quot;&gt;
        {showNavigation &amp;&amp; (
          &lt;React.Fragment&gt;
            &lt;button className=&quot;left-arrow&quot; onClick={scrollLeft}&gt;
              {&#39;&lt;&#39;}
            &lt;/button&gt;
            &lt;button className=&quot;right-arrow&quot; onClick={scrollRight}&gt;
              {&#39;&gt;&#39;}
            &lt;/button&gt;
          &lt;/React.Fragment&gt;
        )}
      &lt;/div&gt;
      &lt;button onClick={add}&gt;ADD&lt;/button&gt;
      &lt;br /&gt;
      &lt;br/&gt;
      &lt;button onClick={remove}&gt;REMOVE&lt;/button&gt;
    &lt;/div&gt;
  );
}


const rootElement = document.getElementById(&#39;root&#39;);
const root = ReactDOM.createRoot(rootElement);
root.render(&lt;App /&gt;);

<!-- language: lang-css -->

.d-flex {
  display: flex;
}

.outer-container {
  width: 100%;
  overflow-x: auto;
}

.inner-container {
  width: max-content;
}

.boxes {
  padding: 12px;
  margin: 4px;
  background-color: rgb(196, 218, 243);
  min-width: 150px;
  text-align: center;
  border-radius: 6px;
}

.arrow-container {
  height: 2em;
}

.right-arrow {
  position: absolute;
  right: 8px;
}

<!-- language: lang-html -->

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;

<!-- end snippet -->


Stackblitz Typescript example

https://stackblitz.com/edit/react-ts-rq7fnm?file=App.tsx

答案2

得分: 1

你可以使用 react-magic-slider-dots npm 包,并使用自己的 CSS 来匹配你的设计。我已经为你实现了这一点,可以参考这个CodeSandbox示例

英文:

You can use a react-magic-slider-dots npm package and use your own CSS to match your design.
I have implemented this for your reference. Please have a look at the working code sandbox example.

答案3

得分: 0

Solve the problem first. Code next.

Solution

观察容器 div,第一个框的 div 和最后一个框的 div,并相应地更改用户界面。

通过比较与容器的位置(它们是否粘在那里)来保持布尔值(简单的变量即可,不需要状态)以检查左按钮和右按钮是否可见或不可见。

Code (idea)

使用 ref 来获取 DOM 节点,(而不是 document.getElementById,你知道的)。使用 myRef.current.getBoundingClientRect() 获取它们的位置和尺寸。

伪代码:

<div ref={containerRef}>
  {myArray.map((item, idx) => <div ref={idx === 0 ? firstBoxRef : (idx === myArray.length - 1 ? lastBoxRef : null)}>...</div>}
</div>

当然,你需要添加按钮、overflowmax-width 属性以及 onClick 滚动

附注:ref 的一个作用是作为 document.getElementById 的替代品。

英文:

Solve the problem first. Code next.

Solution

Observe the container div, the first box's div, and the last box's div and change the UI accordingly.

Keep booleans (simple variables are fine, state not needed) to check if the left button and right button are visible or not, by comparing against the container (if they are sticking or not).

Code (idea)

Use ref to get the DOM nodes, (instead of document.getElem..., you know). Get their position and dimensions using myRef.current.getBoundingClientRect().

Pseudocode:

&lt;div ref={containerRef}&gt;
  {myArray.map((item, idx) =&gt; &lt;div ref={idx === 0 firstBoxRef ? (idx === myArray.length - 1 ? lastBoxRef : null)}&gt;...&lt;/div&gt;}
&lt;/div&gt;

You'll need to add the buttons, overflow, max-width properties and the onClick scroll, of course.

PS: One purpose of ref is to act as an alternative to document.getElementById.

答案4

得分: 0

请尝试使用Swiper,它通过其Web组件覆盖了这个用例。

以下是示例:

使用点:
https://codesandbox.io/p/sandbox/uney2m?file=%2Findex.html

使用动态点:
https://codesandbox.io/p/sandbox/49kqyp?file=%2Findex.html

使用pagination clickable属性以启用移动到相应幻灯片的功能,如此示例中所示
https://codesandbox.io/p/sandbox/xp5tzz?file=%2Findex.html

随意探索其他类型
https://swiperjs.com/demos

英文:

Try using Swiper, it covers this use case with its web components.

Here is the samples:

Using dots:
https://codesandbox.io/p/sandbox/uney2m?file=%2Findex.html

Using dynamic dots:
https://codesandbox.io/p/sandbox/49kqyp?file=%2Findex.html

Use the pagination clickable props to enable the functionality of moving to respective slide as shown in this example
https://codesandbox.io/p/sandbox/xp5tzz?file=%2Findex.html

Feel free to explore other types
https://swiperjs.com/demos

huangapple
  • 本文由 发表于 2023年4月6日 20:40:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/75949631.html
匿名

发表评论

匿名网友

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

确定