简体   繁体   English

"如何使 Material-ui-next 中的 AppBar 组件对滚动事件做出反应"

[英]How to make AppBar component from material-ui-next react to scroll events

As per Material Design guidelines :根据材料设计指南

Upon scrolling, the top app bar can […] transform in the following ways:滚动后,顶部应用栏可以 [...] 以下列方式转换:
- Scrolling upward hides the top app bar - 向上滚动隐藏顶部应用栏
- Scrolling downward reveals the top app bar - 向下滚动显示顶部应用栏
When the top app bar scrolls, its elevation above other elements becomes apparent.当顶部应用栏滚动时,它在其他元素之上的高度变得明显。

Is there any built-in approach to do this in material-ui-next or should it be considered as a new feature?在 material-ui-next 中是否有任何内置方法可以做到这一点,还是应该将其视为新功能? Can you give a hint on how to achieve the animation of the AppBar component as described in the guidelines?能否给出指南中描述的如何实现 AppBar 组件动画的提示?

To my knowledge, there's no out-of-the-box solution for this at the moment.据我所知,目前没有现成的解决方案。 It's quite easy to implement though.不过它很容易实现。 Here is a snippet that subscribes to scroll events and hides or shows the AppBar accordingly:这是订阅滚动事件并相应地隐藏或显示 AppBar 的片段:

const styles = {
  root: {
    flexGrow: 1,
  },
  show: {
    transform: 'translateY(0)',
    transition: 'transform .5s',
  },
  hide: {
    transform: 'translateY(-110%)',
    transition: 'transform .5s',
  },
};

class CollapsibleAppBar extends React.PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      shouldShow: null,
    };

    this.lastScroll = null;

    this.handleScroll = this.handleScroll.bind(this);
    // Alternatively, you can throttle scroll events to avoid
    // updating the state too often. Here using lodash.
    // this.handleScroll = _.throttle(this.handleScroll.bind(this), 100);
  }

  componentDidMount() {
    window.addEventListener('scroll', this.handleScroll, { passive: true });
  }

  componentWillUnmount() {
    window.removeEventListener('scroll', this.handleScroll);
  }

  handleScroll(evt) {
    const lastScroll = window.scrollY;

    if (lastScroll === this.lastScroll) {
      return;
    }

    const shouldShow = (this.lastScroll !== null) ?  (lastScroll < this.lastScroll) : null;

    if (shouldShow !== this.state.shouldShow) {
      this.setState((prevState, props) => ({
        ...prevState,
        shouldShow,
      }));
    }

    this.lastScroll = lastScroll;
  }

  render() {
    const { classes } = this.props;
    return (
        <AppBar
      position="fixed"
      color="default"
      className={
            `${classes.root} ${
              this.state.shouldShow === null ? '' : (
                this.state.shouldShow ? classes.show : classes.hide
              )
            }`
          }
    >
          <Toolbar>
            <Typography variant="title" color="inherit">
              Title
            </Typography>
          </Toolbar>
        </AppBar>
    );
  }
}

CollapsibleAppBar.propTypes = {
  classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(CollapsibleAppBar);

in the current version of Material-ui, you can simply use the following在当前版本的 Material-ui 中,您可以简单地使用以下内容

import clsx from "clsx";
import useScrollTrigger from "@material-ui/core/useScrollTrigger";
const trigger = useScrollTrigger();

<AppBar className={trigger ? classes.show : classes.hide}>
</AppBar>

https://material-ui.com/components/app-bar/#usescrolltrigger-options-trigger https://material-ui.com/components/app-bar/#usescrolltrigger-options-trigger

对于那些仍在寻找内置功能的人,可以在material-ui找到滚动时隐藏应用栏

this seem to work for me这似乎对我有用

import {
  useScrollTrigger,
  Fab,
  Zoom,
} from '@mui/material';

... ...

function ElevationScroll(props) {
  const { children } = props;
  const theme = useTheme();
  const trigger = useScrollTrigger({
    disableHysteresis: true,
    threshold: 0,
  });

  return React.cloneElement(children, {
    sx: trigger
      ? {
          bgcolor: theme.palette.primary.dark,
          'transition-duration': '500ms',
          'transition-property':
            'padding-top, padding-bottom, background-color',
          'transition-timing-function': 'ease-in-out',
        }
      : {
          pt: 2,
          pb: 2,
          bgcolor: theme.palette.primary.main,
        },
    elevation: trigger ? 5 : 0,
  });
}

function ScrollTop(props) {
  const { children } = props;
  const trigger = useScrollTrigger({
    disableHysteresis: true,
    threshold: 200,
  });

  const handleClick = (event) => {
    const anchor = (event.target.ownerDocument || document).querySelector(
      '#back-to-top-anchor'
    );

    if (anchor) {
      anchor.scrollIntoView({
        behavior: 'smooth',
        block: 'center',
      });
    }
  };

  return (
    <Zoom in={trigger}>
      <Box
        onClick={handleClick}
        role="presentation"
        sx={{ position: 'fixed', bottom: 16, right: 16, zIndex: 1 }}
      >
        {children}
      </Box>
    </Zoom>
  );
}

... ...

  return (
    <React.Fragment>
      <ElevationScroll {...props}>
        <AppBar position="sticky">
        ...
        </AppBar>
      </ElevationScroll>
      <Toolbar
        id="back-to-top-anchor"
        className="_Toolbar"
        sx={{
          minHeight: '0 !important',
        }}
      />
      <ScrollTop {...props}>
        <Fab color="secondary" size="small" aria-label="scroll back to top">
          <KeyboardArrowUpIcon />
        </Fab>
      </ScrollTop>
    </React.Fragment>

this seem to work for me这似乎对我有用

import {
  useScrollTrigger,
  Fab,
  Zoom,
} from '@mui/material';

... ...

function ElevationScroll(props) {
  const { children } = props;
  const theme = useTheme();
  const trigger = useScrollTrigger({
    disableHysteresis: true,
    threshold: 0,
  });

  return React.cloneElement(children, {
    sx: trigger
      ? {
          bgcolor: theme.palette.primary.dark,
          'transition-duration': '500ms',
          'transition-property':
            'padding-top, padding-bottom, background-color',
          'transition-timing-function': 'ease-in-out',
        }
      : {
          pt: 2,
          pb: 2,
          bgcolor: theme.palette.primary.main,
        },
    elevation: trigger ? 5 : 0,
  });
}

function ScrollTop(props) {
  const { children } = props;
  const trigger = useScrollTrigger({
    disableHysteresis: true,
    threshold: 200,
  });

  const handleClick = (event) => {
    const anchor = (event.target.ownerDocument || document).querySelector(
      '#back-to-top-anchor'
    );

    if (anchor) {
      anchor.scrollIntoView({
        behavior: 'smooth',
        block: 'center',
      });
    }
  };

  return (
    <Zoom in={trigger}>
      <Box
        onClick={handleClick}
        role="presentation"
        sx={{ position: 'fixed', bottom: 16, right: 16, zIndex: 1 }}
      >
        {children}
      </Box>
    </Zoom>
  );
}

... ...

  return (
    <React.Fragment>
      <ElevationScroll {...props}>
        <AppBar position="sticky">
        ...
        </AppBar>
      </ElevationScroll>
      <Toolbar
        id="back-to-top-anchor"
        className="_Toolbar"
        sx={{
          minHeight: '0 !important',
        }}
      />
      <ScrollTop {...props}>
        <Fab color="secondary" size="small" aria-label="scroll back to top">
          <KeyboardArrowUpIcon />
        </Fab>
      </ScrollTop>
    </React.Fragment>

https://mui.com/components/app-bar/#usescrolltrigger-options-trigger https://mui.com/components/app-bar/#usescrolltrigger-options-trigger

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM