我目前正面临一个滚动效果问题,我正在尝试在 React 中实现该效果。
这是想要的效果,我可以使用ChatGPT在React中实现它,只要所有面板都有100vh的固定高度,例如滚动效果就像Inteled一样。
对于演示,我设置了这个 codesandbox 示例
问题是
double-panel
类的 div 高度为 200vh,由于 fixed
和 panel-fixed
的 panel-inner
位置,这似乎破坏了滚动效果。
一旦到达高度超过 100vh 的
panel
,我应该能够滚动它,而无需修复它,并且只有在其底部到达页面底部后才应修复它。
我该如何进行?
您想要比较底部 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>