滚动在元素中滚动时跳跃内容/不自然移动的宽度增加

发布于 2025-02-11 23:53:39 字数 2974 浏览 1 评论 0原文

我找不到这个问题的答案,但是我已经在许多应用程序(日历,议程等)中看到了这种确切的行为。正如您在我的容器下方的摘要中看到的那样,随着滚动向两侧的滚动 - 新div s插入内部。但是,当您向右滚动时,感觉还可以和自然,但是,当您向左滚动时,它总是会添加元素,并且保持0px需要稍微向后滚动,然后再次向左滚动扩展更多。最好在下面尝试:

import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom/client';

function Test() {
  const [span, setSpan] = useState<Array<number>>([-1, 0, 1]);

  // Append item to the array - scrolling right
  const append = () => {
    setSpan([
      ...span,
      span[span.length - 1] + 1,
    ]);
  };

  // Prepend item to the array - scrolling left
  const prepend = () => {
    setSpan([
      span[0] - 1,
      ...span,
    ]);
  };

  // Center view on load - to the middle of element '0' - e.i. the center
  useEffect(() => {
    const element = document.getElementById('element-0');
    if (element) {
      element.scrollIntoView({ behavior: 'auto', inline: 'center' });
    }
  }, []);

  // Register 'scroll' listener
  useEffect(() => {
    const element = document.getElementById('container');
    const scrolling = () => {
      if (element) {
        if (element.scrollLeft === 0) {
          prepend();
        }
        if (element.offsetWidth + element.scrollLeft >= (element.scrollWidth - 100)) {
          append();
        }
      }
    };
    element.addEventListener('scroll', scrolling);
    return () => {
      element.removeEventListener('scroll', scrolling);
    };
  }, [span.length]);

  return (
    <div style={{
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}
    >
      <div
        id="container"
        style={{
          maxWidth: '50vw', maxHeight: '50vh', overflowX: 'auto', whiteSpace: 'nowrap', backgroundColor: 'red',
        }}
      >
        <div style={{ width: 'fit-content' }}>
          <div style={{ width: 'fit-content' }}>
            <div style={{ display: 'flex' }}>
              {span.map((element) => (
                <div key={`element-${element}`} id={`element-${element}`} style={{ minWidth: '40vw', minHeight: '100vh', border: '1px solid black' }}>
                  { element }
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

const root = ReactDOM.createRoot(
  document.getElementById('root')
);
root.render(
  <React.StrictMode>
    <Test />
  </React.StrictMode>
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

在准备新项目之前,我尝试在编程中滚动一些滚动,但它只会引起更多问题。有一种简单的解决方法吗?

I haven't been able to find an answer to this question but I have seen this exact behaviour in many apps (calendars, agendas etc.). As you can see in the snippet below my container expands with scrolling to both sides - new divs are being inserted inside. When you scroll to the right it feels okay and natural, however, when you scroll to the left, it always adds the element and you stay at 0px needing to scroll a bit back and then to the left again to expand some more. Best if you try below:

import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom/client';

function Test() {
  const [span, setSpan] = useState<Array<number>>([-1, 0, 1]);

  // Append item to the array - scrolling right
  const append = () => {
    setSpan([
      ...span,
      span[span.length - 1] + 1,
    ]);
  };

  // Prepend item to the array - scrolling left
  const prepend = () => {
    setSpan([
      span[0] - 1,
      ...span,
    ]);
  };

  // Center view on load - to the middle of element '0' - e.i. the center
  useEffect(() => {
    const element = document.getElementById('element-0');
    if (element) {
      element.scrollIntoView({ behavior: 'auto', inline: 'center' });
    }
  }, []);

  // Register 'scroll' listener
  useEffect(() => {
    const element = document.getElementById('container');
    const scrolling = () => {
      if (element) {
        if (element.scrollLeft === 0) {
          prepend();
        }
        if (element.offsetWidth + element.scrollLeft >= (element.scrollWidth - 100)) {
          append();
        }
      }
    };
    element.addEventListener('scroll', scrolling);
    return () => {
      element.removeEventListener('scroll', scrolling);
    };
  }, [span.length]);

  return (
    <div style={{
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}
    >
      <div
        id="container"
        style={{
          maxWidth: '50vw', maxHeight: '50vh', overflowX: 'auto', whiteSpace: 'nowrap', backgroundColor: 'red',
        }}
      >
        <div style={{ width: 'fit-content' }}>
          <div style={{ width: 'fit-content' }}>
            <div style={{ display: 'flex' }}>
              {span.map((element) => (
                <div key={`element-${element}`} id={`element-${element}`} style={{ minWidth: '40vw', minHeight: '100vh', border: '1px solid black' }}>
                  { element }
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

const root = ReactDOM.createRoot(
  document.getElementById('root')
);
root.render(
  <React.StrictMode>
    <Test />
  </React.StrictMode>
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

I tried programatically scrolling a bit to the right before prepending new item, but it only created more issues. Is there an easy way to solve it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

玩心态 2025-02-18 23:53:39

准备元素不会使其容器的scrollleft增加元素的宽度。

相反,scrollleft的值保持不变,因此新框有效地“ pops”到视图中:

此处您提到的是,scrollleft在插入后保持零,鼠标轮的移动不会导致容器的滚动,因此scroll未触发,因此框未评估插入逻辑。

例如,可以通过收听 <代码来解决。 >车轮 事件而不是滚动。问题是,scrollleft仍然保持在零,因此盒子只会一一出现在视图中,而不是让用户滚动到它们上。 demo 。另外,鼠标车轮不是滚动的唯一方法。

因此,根据问题的定义,我们需要手动调整滚动位置,以使视图保持与插入之前相同的元素。在这种情况下,此数量仅仅是 /code> 框的,因此解决方案可以如下:

demo

const boxWidth = document.getElementById("element-0").offsetWidth;
if (element.scrollLeft < 100) {
  element.scrollBy(boxWidth, 0);
  prepend();
}
else if (/*...*/) { 

I hope this answers your question. :)

Prepending an element doesn't make its container's scrollLeft increase by as much as the element's width.

enter image description here

Instead, the scrollLeft's value remains the same, and so the new box effectively "pops" into the view:

enter image description here

Since, as you mentioned, the scrollLeft remains at zero after insertion, further mouse wheel movement doesn't result in the container's scroll, so the scroll event is not fired, and hence the box insertion logic is not evaluated.

That can be solved, for example, by listening for the wheel event rather than the scroll. The problem is, that the scrollLeft would still stay at zero, so the boxes would just appear in the view one by one rather than letting the user scroll onto them. Demo. Plus, the mouse wheel is not the only way to scroll.

As such, by the very definition of the problem, we need to manually adjust the scroll position so that the view remains at the same element as before the insertion. In this case, this amount is simply the offsetWidth of the box, so the solution could be as follows:

Demo

const boxWidth = document.getElementById("element-0").offsetWidth;
if (element.scrollLeft < 100) {
  element.scrollBy(boxWidth, 0);
  prepend();
}
else if (/*...*/) { 

I hope this answers your question. :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文