如何修复React中的重叠滚动效果?

问题描述 投票:0回答:1

我目前正面临一个滚动效果问题,我正在尝试在 React 中实现该效果。

这是想要的效果,我可以使用ChatGPT在React中实现它,只要所有面板都有100vh的固定高度,例如滚动效果就像Inteled一样。

对于演示,我设置了这个 codesandbox 示例

问题是

double-panel
类的 div 高度为 200vh,由于
fixed
panel-fixed
panel-inner
位置,这似乎破坏了滚动效果。

一旦到达高度超过 100vh 的

panel
,我应该能够滚动它,而无需修复它,并且只有在其底部到达页面底部后才应修复它。

我该如何进行?

css reactjs scroll vertical-scrolling custom-scrolling
1个回答
0
投票

您想要比较底部 Y 值:

const panelsY = Array.from(panels).map((panel) => panel.offsetTop + panel.offsetHeight);

…
function updateWindow() {
  const y = _window.scrollY + _window.innerHeight;

const { useEffect, Fragment } = React;

function App() {
  const placeholderText =
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
  useEffect(() => {
    // Set up vars
    const _window = window;
    const panels = document.querySelectorAll(".panel");
    const panelsY = Array.from(panels).map((panel) => panel.offsetTop + panel.offsetHeight);

    // Bind our function to window scroll
    _window.addEventListener("scroll", updateWindow);

    // Update the window
    function updateWindow() {
      const y = _window.scrollY + _window.innerHeight;
      console.log("ye");
      // Loop through our panel positions
      let i;
      for (i = 0; i < panels.length; i++) {
        /* 
            Firstly, we break if we're checking our last panel,
            otherwise we compare if the y position is in between
            two panels
          */
        if (
          i === panels.length - 1 ||
          (y >= panelsY[i] && y <= panelsY[i + 1])
        ) {
          break;
        }
      }

      // Update classes
      panels.forEach((panel, index) => {
        if (index === i) {
          panel.classList.add("panel-fixed");
        } else {
          panel.classList.remove("panel-fixed");
        }
      });
    }

    // Cleanup event listener on component unmount
    return () => {
      _window.removeEventListener("scroll", updateWindow);
    };
  }, []);

  return (
    <Fragment>
      <div className="panel bg-teal-200">
        <div className="panel-inner text-4xl">{placeholderText}</div>
      </div>
      <div className="panel bg-amber-500">
        <div className="panel-inner text-4xl">{placeholderText}</div>{" "}
      </div>
      <div className="panel double-panel bg-red-600">
        <div className="panel-inner text-6xl">
          <p>{placeholderText}</p>
        </div>{" "}
      </div>
      <div className="panel bg-purple-200">
        <div className="panel-inner text-4xl">{placeholderText}</div>{" "}
      </div>
      <div className="panel bg-green-200">
        <div className="panel-inner text-4xl">{placeholderText}</div>{" "}
      </div>
    </Fragment>
  );
}

ReactDOM.createRoot(document.getElementById("app")).render(<App />);
.panel {
  position: relative;
  z-index: 5;
  min-height: 100vh;
}

.panel-fixed {
  z-index: 1;
}

.panel-inner {
  width: 100%;
}

.panel-fixed .panel-inner {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 2;
}

.double-panel {
  position: relative;
  z-index: 5;
  min-height: 200vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js" integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js" integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.tailwindcss.com/3.3.3"></script>

<div id="app"></div>

那么您可能希望避免发生的跳转,双面板的内部内容会被

fix
回到
top: 0
,并且当元素高于
min-height
父级的
.panel
时:

.panel-inner {
  …
  height: 100vh;
  overflow: hidden;
}

.double-panel .panel-inner {
  …
  height: 200vh;
}

const { useEffect, Fragment } = React;

function App() {
  const placeholderText =
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
  useEffect(() => {
    // Set up vars
    const _window = window;
    const panels = document.querySelectorAll(".panel");
    const panelsY = Array.from(panels).map((panel) => panel.offsetTop + panel.offsetHeight);

    // Bind our function to window scroll
    _window.addEventListener("scroll", updateWindow);

    // Update the window
    function updateWindow() {
      const y = _window.scrollY + _window.innerHeight;
      console.log("ye");
      // Loop through our panel positions
      let i;
      for (i = 0; i < panels.length; i++) {
        /* 
            Firstly, we break if we're checking our last panel,
            otherwise we compare if the y position is in between
            two panels
          */
        if (
          i === panels.length - 1 ||
          (y >= panelsY[i] && y <= panelsY[i + 1])
        ) {
          break;
        }
      }

      // Update classes
      panels.forEach((panel, index) => {
        if (index === i) {
          panel.classList.add("panel-fixed");
        } else {
          panel.classList.remove("panel-fixed");
        }
      });
    }

    // Cleanup event listener on component unmount
    return () => {
      _window.removeEventListener("scroll", updateWindow);
    };
  }, []);

  return (
    <Fragment>
      <div className="panel bg-teal-200">
        <div className="panel-inner text-4xl">{placeholderText}</div>
      </div>
      <div className="panel bg-amber-500">
        <div className="panel-inner text-4xl">{placeholderText}</div>{" "}
      </div>
      <div className="panel double-panel bg-red-600">
        <div className="panel-inner text-6xl">
          <p>{placeholderText}</p>
        </div>{" "}
      </div>
      <div className="panel bg-purple-200">
        <div className="panel-inner text-4xl">{placeholderText}</div>{" "}
      </div>
      <div className="panel bg-green-200">
        <div className="panel-inner text-4xl">{placeholderText}</div>{" "}
      </div>
    </Fragment>
  );
}

ReactDOM.createRoot(document.getElementById("app")).render(<App />);
.panel {
  position: relative;
  z-index: 5;
  min-height: 100vh;
}

.panel-fixed {
  z-index: 1;
}

.panel-inner {
  width: 100%;
  height: 100vh;
  overflow: hidden;
}

.panel-fixed .panel-inner {
  position: fixed;
  bottom: 0;
  left: 0;
  z-index: 2;
}

.double-panel {
  position: relative;
  z-index: 5;
  min-height: 200vh;
}

.double-panel .panel-inner {
  width: 100%;
  height: 200vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js" integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js" integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.tailwindcss.com/3.3.3"></script>

<div id="app"></div>

© www.soinside.com 2019 - 2024. All rights reserved.