使用粘性导航栏自动滚动 Flexbox 列
Auto-Scrolling A Flexbox Column With A Sticky NavBar
由于这个问题最容易用例子来解释,这里有一个 CodeSandbox 来说明:https://codesandbox.io/s/github/metal450/react-scroll-sticky. The project is built in React, TailwindCSS, & react-scroll 做平滑滚动。
我正在尝试创建 links 以在 flexbox 中滚动 column/row,并带有粘性导航栏。只要导航栏相对于 Flexbox 的主轴而不是其交叉轴 'sticky' ,它就可以很好地工作。在 CodeSandbox 中,如果输出适当宽(也就是在大型设备上),您会看到侧边栏出现在左侧,并且每个 link 滚动右侧的列,将其对应的项目与容器顶部。
但是,如果您稍微缩小输出直到看到 'small device' 布局,您将能够看到问题所在。现在 'sidebar' 位于顶部,如果您单击导航按钮,它将滚动以便所选项目隐藏在导航栏后面。
这确实有一定道理:滚动条相对于容器滚动,并且该容器的顶部位于粘性导航栏后面。但是,我想要的结果是单击 'item 1' 并让它滚动,以便项目 1 位于 可滚动区域 的顶部(不隐藏在粘性导航栏下方)。
我对 flexboxes 还很陌生,无论我尝试什么,我似乎都无法弄清楚所需的行为。任何帮助将不胜感激。
react-scroll
supports offset
参数,可用于动态计算菜单高度:
import { Link } from 'react-scroll';
function App() {
const items = ['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7'];
const container = "scrollContainer";
const menu = "scrollMenu";
function getScrollOffset() {
if (true/* insert a condition for navbar being on top */) {
return -document.getElementById(menu).offsetHeight;
} else return 0;
}
return (
<main className="flex absolute right-0 bottom-0 left-0 top-0">
<div id={container} className="md:flex-row flex flex-col overflow-auto border border-box" style={{ flexBasis: "65%", flex: 2}}>
<div className="md:w-64 p-3 border sticky top-0 flex-shrink-0 bg-white border-box" id={menu}>
{items.map((item) => <div className="cursor-pointer m-2 underline" key={item}>
<Link to={item} smooth={true} duration={500} spy={true} offset={getScrollOffset()} containerId={container}>{item}</Link>
</div>)}
</div>
<div style={{ flex: 1 }}>
{items.map((item) => <div key={item} id={item} className="h-64 border m-2 bg-gray-100" >{item}</div>)}
</div>
</div>
</main>
);
}
export default App;
仅 CSS 的解决方案远没有那么优雅,并且没有提供特别的好处,因为滚动已经由 JavaScript 端处理。
扩展@YellowAfterlife 的回答,我会使用 refs 而不是 document.getElementById
。
此外,您只需要“移动”上的偏移量,因此您可以使用像 react-responsive
这样的包。
import React, { useRef, useState, useEffect } from "react";
import { Link } from "react-scroll";
import { useMediaQuery } from "react-responsive";
function App() {
const items = ["item1", "item2", "item3", "item4", "item5", "item6", "item7"];
const container = "scrollContainer";
const menuRef = useRef(null);
const [menuHeight, setMenuHeight] = useState(0);
const isMobile = useMediaQuery({
query: "(max-width: 767px)"
});
useEffect(() => {
setMenuHeight(menuRef.current.clientHeight);
}, [menuRef]);
return (
<main className="flex absolute right-0 bottom-0 left-0 top-0">
<div
id={container}
className="md:flex-row flex flex-col overflow-auto border border-box"
style={{ flexBasis: "65%", flex: 2 }}
>
<div
ref={menuRef}
className="md:w-64 p-3 border sticky top-0 flex-shrink-0 bg-white border-box"
>
{items.map((item) => (
<div className="cursor-pointer m-2 underline" key={item}>
<Link
to={item}
smooth={true}
duration={500}
spy={true}
containerId={container}
offset={isMobile ? -menuHeight : null}
>
{item}
</Link>
</div>
))}
</div>
<div style={{ flex: 1 }}>
{items.map((item) => (
<div key={item} id={item} className="h-64 border m-2 bg-gray-100">
{item}
</div>
))}
</div>
</div>
</main>
);
}
export default App;
由于这个问题最容易用例子来解释,这里有一个 CodeSandbox 来说明:https://codesandbox.io/s/github/metal450/react-scroll-sticky. The project is built in React, TailwindCSS, & react-scroll 做平滑滚动。
我正在尝试创建 links 以在 flexbox 中滚动 column/row,并带有粘性导航栏。只要导航栏相对于 Flexbox 的主轴而不是其交叉轴 'sticky' ,它就可以很好地工作。在 CodeSandbox 中,如果输出适当宽(也就是在大型设备上),您会看到侧边栏出现在左侧,并且每个 link 滚动右侧的列,将其对应的项目与容器顶部。
但是,如果您稍微缩小输出直到看到 'small device' 布局,您将能够看到问题所在。现在 'sidebar' 位于顶部,如果您单击导航按钮,它将滚动以便所选项目隐藏在导航栏后面。
这确实有一定道理:滚动条相对于容器滚动,并且该容器的顶部位于粘性导航栏后面。但是,我想要的结果是单击 'item 1' 并让它滚动,以便项目 1 位于 可滚动区域 的顶部(不隐藏在粘性导航栏下方)。
我对 flexboxes 还很陌生,无论我尝试什么,我似乎都无法弄清楚所需的行为。任何帮助将不胜感激。
react-scroll
supports offset
参数,可用于动态计算菜单高度:
import { Link } from 'react-scroll';
function App() {
const items = ['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7'];
const container = "scrollContainer";
const menu = "scrollMenu";
function getScrollOffset() {
if (true/* insert a condition for navbar being on top */) {
return -document.getElementById(menu).offsetHeight;
} else return 0;
}
return (
<main className="flex absolute right-0 bottom-0 left-0 top-0">
<div id={container} className="md:flex-row flex flex-col overflow-auto border border-box" style={{ flexBasis: "65%", flex: 2}}>
<div className="md:w-64 p-3 border sticky top-0 flex-shrink-0 bg-white border-box" id={menu}>
{items.map((item) => <div className="cursor-pointer m-2 underline" key={item}>
<Link to={item} smooth={true} duration={500} spy={true} offset={getScrollOffset()} containerId={container}>{item}</Link>
</div>)}
</div>
<div style={{ flex: 1 }}>
{items.map((item) => <div key={item} id={item} className="h-64 border m-2 bg-gray-100" >{item}</div>)}
</div>
</div>
</main>
);
}
export default App;
仅 CSS 的解决方案远没有那么优雅,并且没有提供特别的好处,因为滚动已经由 JavaScript 端处理。
扩展@YellowAfterlife 的回答,我会使用 refs 而不是 document.getElementById
。
此外,您只需要“移动”上的偏移量,因此您可以使用像 react-responsive
这样的包。
import React, { useRef, useState, useEffect } from "react";
import { Link } from "react-scroll";
import { useMediaQuery } from "react-responsive";
function App() {
const items = ["item1", "item2", "item3", "item4", "item5", "item6", "item7"];
const container = "scrollContainer";
const menuRef = useRef(null);
const [menuHeight, setMenuHeight] = useState(0);
const isMobile = useMediaQuery({
query: "(max-width: 767px)"
});
useEffect(() => {
setMenuHeight(menuRef.current.clientHeight);
}, [menuRef]);
return (
<main className="flex absolute right-0 bottom-0 left-0 top-0">
<div
id={container}
className="md:flex-row flex flex-col overflow-auto border border-box"
style={{ flexBasis: "65%", flex: 2 }}
>
<div
ref={menuRef}
className="md:w-64 p-3 border sticky top-0 flex-shrink-0 bg-white border-box"
>
{items.map((item) => (
<div className="cursor-pointer m-2 underline" key={item}>
<Link
to={item}
smooth={true}
duration={500}
spy={true}
containerId={container}
offset={isMobile ? -menuHeight : null}
>
{item}
</Link>
</div>
))}
</div>
<div style={{ flex: 1 }}>
{items.map((item) => (
<div key={item} id={item} className="h-64 border m-2 bg-gray-100">
{item}
</div>
))}
</div>
</div>
</main>
);
}
export default App;