问题
I try to figure out if it is possible to hide only the upper part of the nav-bar. See Img below (green marked area). This should mimic the navbar behaviour like the whatsapp-app.
I am using material-ui for this use-case. With my implementation the app-bar only extends again if the scroll position is < 48px. On the .gif-file (see below) it extends on every scroll up event. It also looks like the app-bar scrolls only first until it reaches position fixed. After then the rest of the content begins to scroll.
Edit
I implemented a proof-of-concept, but it is not quite working as expected: stackblitz
My approach looks as follows:
export default function TabBar() {
const [value, setValue] = React.useState(0);
const [yOffset, setYOffset] = React.useState(0);
function handleChange(event: React.ChangeEvent<{}>, newValue: number) {
setValue(newValue);
}
function transitionY() {
const transitionYthreshold = 48;
return Math.min(transitionYthreshold, yOffset);
}
useEffect(() => {
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
});
function handleScroll() {
setYOffset(window.pageYOffset);
}
return (
<React.Fragment>
<AppBar
position="sticky"
color="default"
style={{
transition: 'all 0.1s',
transform: `translateY(-${transitionY()}px)`
}}
>
<Toolbar style={{ minHeight: '48px' }}>
<div style={{ width: '30px', marginRight: '1em' }} />
<span style={{ fontWeight: 'bold', fontSize: '20px', verticalAlign: 'super' }}>Help-Educate</span>
</Toolbar>
<Tabs
value={value}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
variant="fullWidth"
>
<Tab label="Home" {...a11yProps(0)}/>
<Tab label="Donations" {...a11yProps(1)}/>
<Tab label="About Us" {...a11yProps(2)}/>
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
<Container>
{**SomeSuperLongText**}
</Container>
</TabPanel>
<TabPanel value={value} index={1}>
{**SomeSuperLongText**}
</TabPanel>
<TabPanel value={value} index={2}>
{**SomeSuperLongText**}
</TabPanel>
</React.Fragment>
);
}
I created a gif how the behaviour should look like: dropbox-link
回答1:
Most probably it is not the most elegent solution, but after trying around I came up with the following way:
import React from "react";
import PropTypes from "prop-types";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import CssBaseline from "@material-ui/core/CssBaseline";
import useScrollTrigger from "@material-ui/core/useScrollTrigger";
import Box from "@material-ui/core/Box";
import Container from "@material-ui/core/Container";
import Slide from "@material-ui/core/Slide";
function HideOnScroll(props) {
const { children } = props;
const trigger = useScrollTrigger({
threshold: 0
});
return (
<Slide appear={false} direction="down" in={!trigger}>
{children}
</Slide>
);
}
HideOnScroll.propTypes = {
children: PropTypes.element.isRequired
};
export default function HideAppBar(props) {
return (
<React.Fragment>
<CssBaseline />
<HideOnScroll {...props}>
<AppBar>
<Toolbar>
<Typography variant="h6">Scroll to Hide App Bar</Typography>
</Toolbar>
</AppBar>
</HideOnScroll>
<Toolbar />
<AppBar position="sticky">
<Toolbar>
<Typography variant="h6">Bar will stay</Typography>
</Toolbar>
</AppBar>
<Container>
<Box my={2}>
{[...new Array(20)]
.map(
() => `Cras mattis consectetur purus sit amet fermentum.
Cras justo odio, dapibus ac facilisis in, egestas eget quam.
Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
Praesent commodo cursus magna, vel scelerisque nisl consectetur et.`
)
.join("\n")}
</Box>
</Container>
</React.Fragment>
);
}
--> Just put another sticky app bar in your content container and modify the useScrollTrigger with threshold option
See it here: https://codesandbox.io/s/serverless-cache-rcxen
回答2:
Here is my solution: https://stackblitz.com/edit/react-ts-azrqoy
I am making the Container with text scrollable (I have left the scrollbars visible, so you can see better what happens, but you can easily remove them with css)
<Container
style={{
top:48,
paddingTop:48,
bottom: - 48,
scrollTop: yOffset - transitionY(),
pointerEvents: transitionY()<48?"none":"auto"
}} className="cont" onScroll={handleInsideScroll}>
The trick is to stop pointer-events
for the container if you want fingers/scrolling to move the page scroll and enable it back if you want to scroll inside. So this is pretty much CSS solution and I personally prefer these more than pushing values with JS - they are smoother and more optimised.
There are two problems:
- when the pointer-events switch happens, you have to move your mouse (1px) to have the scroll work again (This can probably be fixed if you ref
and manually change containerRef.style.pointerEvents
instead of setting states and rerendering components.
- the logic of this script doesn't handle scrolling up exactly like your gif animation, you will probably have to detect scrolling up and stop the pointer-events until the bar shows
I have never written a line in TypeScript, so I couldn't really try everything I wanted.
回答3:
I have seen the whatsapp navbar and I understood what you wanted.
You can actually use the window.pageYOffset
and set the style.top
of the navbar
in the window.onscroll
function.
A sample is shown in the w3schools website which is working as the way you mentioned.
来源:https://stackoverflow.com/questions/57945569/css-html-scroll-header-before-content