代码之家  ›  专栏  ›  技术社区  ›  Summer Developer

在滚动事件上响应更改图像源

  •  2
  • Summer Developer  · 技术社区  · 8 年前

    作为一个刚开始使用react但熟悉javascript的人,我很难确切地知道在给定的react组件中应该将某些功能放在何处,并且不确定我遇到的这个问题是react逻辑的问题还是加载图像时的延迟时间,虽然如果是后者,我不理解这一点,因为我将图像预加载到一个隐藏的div中。

    我期望的工作流如下:

    1. 加载两个图像,其中一个“预加载” height:0 作为 hide 上课。
    2. 当窗口滚动时,有效地切换 隐藏 对这两个图像中的任何一个初始化,以便根据滚动位置有效地更改显示的图像。当这种情况发生时,标题的背景色也会改变。

    在我的组件中 Header.jsx 我有以下资料:

    componentDidMount() {
        if (this.props.changeColorOnScroll) {
          window.addEventListener("scroll", this.headerColorChange);
        }
      }
      headerColorChange() {
        console.log("Scroll Fire!");
        const { classes, color, changeColorOnScroll} = this.props;
        const windowsScrollTop = window.pageYOffset;
        if (windowsScrollTop > changeColorOnScroll.height) {
          document.body
            .getElementsByTagName("header")[0]
            .classList.remove(classes[color]);
          document.body
            .getElementsByTagName("header")[0]
            .classList.add(classes[changeColorOnScroll.color]);
         document
            .getElementById("headerLogoContainer").getElementsByClassName("logo")[0]
            .classList.add('hide');
         document
            .getElementById("headerLogoContainer").getElementsByClassName("logoalt")[0]
            .classList.remove('hide');
        } else {
          document.body
            .getElementsByTagName("header")[0]
            .classList.add(classes[color]);
          document.body
            .getElementsByTagName("header")[0]
            .classList.remove(classes[changeColorOnScroll.color]);
         document
            .getElementById("headerLogoContainer").getElementsByClassName("logo")[0]
            .classList.remove('hide');
         document
            .getElementById("headerLogoContainer").getElementsByClassName("logoalt")[0]
            .classList.add('hide');
        }
      } 
    

    我相信这是连接事件侦听器的正确位置 componentDidMount() 是的。我的问题是,当我滚动时,窗口会随着图像的变化而冻结,即使我在此处预加载了图像:

    render() {
        const {
          classes,
          color,
          rightLinks,
          leftLinks,
          brand,
          logo,
          logoalt,
          fixed,
          absolute
        } = this.props;
        const appBarClasses = classNames({
          [classes.appBar]: true,
          [classes[color]]: color,
          [classes.absolute]: absolute,
          [classes.fixed]: fixed
        });
        let brandComponent;
        if(!logo){
        brandComponent = (
          <Button className={classes.title}>
            {brand}
          </Button>
        );
        }else{
        brandComponent = (
          <div id='headerLogoContainer'>
          <img className='logo'  src={logo}></img>
          <img className='logoalt hide'  src={logoalt}></img>
          </div>
        );
        }
        return (
          <AppBar className={appBarClasses}>
            <Toolbar className={classes.container}>
              {leftLinks !== undefined ? brandComponent : null}
              <div className={classes.flex}>
                {leftLinks !== undefined ? (
                  <Hidden smDown implementation="css">
                    {leftLinks}
                  </Hidden>
                ) : (
                  brandComponent
                )}
              </div>
    

    如你所见,在 组件didmount() 我设置事件侦听器,然后在渲染中设置 <div id='headerLogoContainer'> 作为我的一部分 brandComponent 是的。我看到了 品牌成分 成功渲染到dom,但在第一个滚动(向下)时,它锁定屏幕(在chrome中)或不显示 logoalt 马上(在edge和firefox中)。几秒钟后,图像切换,然后我可以向上或向下滚动,切换没有问题。这使我认为这可能是一个预加载问题,但为什么当我在 品牌成分 在教堂里看。没有其他网络请求,只需切换 隐藏 要显示的图像。

    所以我的问题是,如何在react中根据滚动事件更改图像源,而不延迟或冻结滚动位置?

    编辑:

    我已经根据建议的答案更改了代码以利用内部状态,当图像更改时,它仍然具有相同的延迟效果:

    class Header extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          mobileOpen: false,
          logoaltState: false
        };
        this.handleDrawerToggle = this.handleDrawerToggle.bind(this);
        this.headerColorChange = this.headerColorChange.bind(this);
      }
      handleDrawerToggle() {
        this.setState({ mobileOpen: !this.state.mobileOpen });
      }
      componentDidMount() {
        if (this.props.changeColorOnScroll) {
          window.addEventListener("scroll", this.headerColorChange);
        }
      }
      headerColorChange() {
        console.log("Scroll Fire!");
        const { classes, color, changeColorOnScroll} = this.props;
        const windowsScrollTop = window.pageYOffset;
        if (windowsScrollTop > changeColorOnScroll.height) {
          document.body
            .getElementsByTagName("header")[0]
            .classList.remove(classes[color]);
          document.body
            .getElementsByTagName("header")[0]
            .classList.add(classes[changeColorOnScroll.color]);
            this.setState({ logoaltState: true });
        } else {
          document.body
            .getElementsByTagName("header")[0]
            .classList.add(classes[color]);
          document.body
            .getElementsByTagName("header")[0]
            .classList.remove(classes[changeColorOnScroll.color]);
          this.setState({ logoaltState: false });
        }
      }
    

    在我看来…

    render() {
        const {
          classes,
          color,
          rightLinks,
          leftLinks,
          brand,
          logo,
          logoalt,
          fixed,
          absolute
        } = this.props;
        const{
            mobileOpen,
            logoaltState
        } = this.state
        const appBarClasses = classNames({
          [classes.appBar]: true,
          [classes[color]]: color,
          [classes.absolute]: absolute,
          [classes.fixed]: fixed
        });
        let brandComponent;
        if(!logo){
        brandComponent = (
          <Button className={classes.title}>
            {brand}
          </Button>
        );
        }else{
        brandComponent = (
          <div id='headerLogoContainer'>
          <img className='logo'  src={logo} style={{display: logoaltState === false ? "block" : "none"}}></img>
          <img className='logoalt'   src={logoalt} style={{display: logoaltState === true ? "block" : "none"}}></img>
          </div>
        );
        }
    

    不同的代码/方法,相同的问题…

    编辑2:

    我不认为这是应用程序逻辑的问题,因为正如我所提到的,它只是冻结 第一次 图像/状态需要更改。我认为这是一种首次加载问题,即使我没有任何网络请求,也许我包括图像源错误?或者在react中,图像需要通过其他方法预加载,因为当包含图像源时,它会稍微关闭…即 /static/media/logo.ad924167.png 作为反对 require("assets/img/tovlogo.png" 我认为这是因为我的节点服务器设置,但再次,我不确定是怎么回事,因为我是相当新的生态系统…我用的是 Material UI React Template ,这就是此组件的派生位置。

    主题文件本身是这样的: https://www.creative-tim.com/product/material-kit-react?partner=104080 所以我想知道它是如何处理图像的 src require …但同样没有对图像的额外要求…只是css/样式的改变…

    编辑3:

    这就是我的调试器(chrome:performance)在冻结时的样子:

    debugger

    我意识到在油漆时间,图像是相当大的,但不应该预装地址…我想不是因为那是一个网络预加载不是一个油漆预加载 display:none;" …也许我可以试试 opacity 而是css属性,但这会使其他css复杂化…总的来说有更好的方法吗?

    最终编辑:

    我的图像超过10000像素宽…我用它作为一个标志,所以直到我做了性能指标,才意识到它有多糟糕…我猜 是它冻结的原因…抱歉,反应过来。:。(

    1 回复  |  直到 7 年前
        1
  •  5
  •   Tholle    8 年前

    与其手动更改dom元素上的类,不如利用组件状态并根据滚动位置在活动图像之间切换,这样对您来说会更容易。

    例子

    class App extends React.Component {
      state = { active: 0 };
    
      componentDidMount() {
        window.addEventListener("scroll", this.handleScroll);
      }
    
      componentWillUnmount() {
        window.removeEventListener("scroll", this.handleScroll);
      }
    
      handleScroll = event => {
        const { pageYOffset } = window;
        const { active } = this.state;
    
        if (pageYOffset >= 500 && active === 0) {
          this.setState({ active: 1 });
        } else if (pageYOffset < 500 && active === 1) {
          this.setState({ active: 0 });
        }
      };
    
      render() {
        const { active } = this.state;
        return (
          <div style={{ height: 1000, width: 1000 }}>
            <div
              style={{
                height: "100%",
                width: "100%",
                backgroundColor: "red",
                display: active === 0 ? "block" : "none"
              }}
            />
            <div
              style={{
                height: "100%",
                width: "100%",
                backgroundColor: "blue",
                display: active === 1 ? "block" : "none"
              }}
            />
          </div>
        );
      }
    }
    
    ReactDOM.render(<App />, document.getElementById("root"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <div id="root"></div>