reactjs处理元素的滚动以查看其是否可见

时间:2019-05-01 02:18:32

标签: javascript reactjs

我不知道为什么handleScroll只执行一次,而不是在我上下滚动时不执行一次。而且我需要以某种方式获取元素的高度,但我不确定除了documentElement之外的其他方式。该函数需要设置为true / false,以便我可以执行setState并在heroGallery div中添加/更改css动画的类。

 import React, { Component } from "react";

 class Intro extends Component {
constructor(props) {
    super(props);
    this.heroRef = React.createRef();
    this.state = {};
}

componentDidMount = () => {
    this.heroRef.current.getBoundingClientRect();
    let hero2 = this.heroRef;
    console.log(this.heroRef.current);
    window.addEventListener("scroll", this.handleScroll(hero2));
};

componentWillUnmount = () => {
    window.removeEventListener("scroll", this.handleScroll);
};

handleScroll = elm => {
    var rect = elm.current.getBoundingClientRect();
    //var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);

    //return !(rect.bottom < 0 || rect.top - viewHeight >= 0);
};

render() {
    return (
        <div className="introCont">
            <div className="introFirstSection" />
            <div className="heroSection">
                <div className="heroGallery" ref={this.heroRef}>
                    <div className="slide-down">item 1</div>
                    <div>item 2</div>
                    <div>item 3</div>
                    <div>item 4</div>
                    <div>item 5</div>
                    <div>item 6</div>
                    <div>item 7</div>
                    <div>item 8</div>
                </div>
            </div>
        </div>
    );
  }
 }

 export default Intro;

编辑: 从Switch中取出我的简介组件可以使其正常工作。

import React, { lazy, Suspense, Component } from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { connect } from "react-redux";
import * as actions from "./actions";

//import asyncComponent from "./components/AsyncComponent";
//const Header = asyncComponent(() => import("./Header"));
import Intro from "./components/Intro";
const Header = lazy(() => import("./components/Header"));
const Footer = lazy(() => import("./components/Footer"));
const Landing = lazy(() => import("./components/Landing"));
const Profile = lazy(() => import("./components/Profile"));
const BookList = lazy(() => import("./components/books/BookList"));
const BookNote = lazy(() => import("./components/books/BookNote"));
const StatsChart = lazy(() => import("./components/StatsChart"));

class App extends Component {
  componentDidMount() {
    this.props.fetchUser();
  }

  render() {
    return (
      <BrowserRouter>
        <div className="rootdk">
          <Suspense fallback={<div />}>
            <Header />
          </Suspense>
          <div className="container">
            <Suspense fallback={<div className="loader" />}>
              <Switch>
                <Route exact path="/intro" component={() => <Intro />} />

                <Route
                  exact
                  path="/mybooks"
                  component={() =>
                    this.props.thisuser ? <BookList /> : <Landing />
                  }
                />
                <Route
                  exact
                  path="/booknotes"
                  component={() =>
                    this.props.thisuser ? <BookNote /> : <Landing />
                  }
                />
                <Route
                  exact
                  path="/statschart"
                  component={() =>
                    this.props.thisuser ? <StatsChart /> : <Landing />
                  }
                />
                <Route
                  exact
                  path="/profile"
                  component={() =>
                    this.props.thisuser ? <Profile /> : <Landing />
                  }
                />
                <Route exact path="/" component={Landing} />
              </Switch>
            </Suspense>

            <Suspense fallback={<div />}>
              <Footer />
            </Suspense>
          </div>
        </div>
      </BrowserRouter>
    );
  }
}

function mapStateToProps(props) {
  return { thisuser: props.auth };
}

export default connect(
  mapStateToProps,
  actions
)(App);

2 个答案:

答案 0 :(得分:2)

这是因为您正在调用addEventListener中的方法。

演示:https://codesandbox.io/s/nv16oq6j?fontsize=14

将您的componentDidMount函数更改为此。

componentDidMount = () => {
    this.heroRef.current.getBoundingClientRect();
    let hero2 = this.heroRef;
    console.log(this.heroRef.current);
    window.addEventListener("scroll", () => this.handleScroll(hero2));
};

我所做的只是将addEventListener的第二个参数作为函数。

答案 1 :(得分:0)

另一个答案是使用HTML5 API,这使它变得容易得多: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

此外,容器显然必须具有固定的高度,并且将溢出属性设置为自动或滚动。

示例:

import React, { Component } from "react";
import ReactDOM from "react-dom";

class Intro extends Component {
  constructor(props) {
    super(props);
    this.heroRef = React.createRef();
    this.state = {
      heroanim: "no"
    };
  }

  componentDidMount = () => {
    setTimeout(() => {
      this.startup();
    }, 500);
    this.startup();
  };

  componentWillUnmount = () => {};

  startup = () => {
    //the class we want to observe for visibility changes, scrolling for animation.
    let adBox = document.querySelector(".heroSection");

    //checks if page was tabbed out or hidden. separate from intersectionobserver
    document.addEventListener(
      "visibilitychange",
      this.handleVisibilityChange,
      false
    );
    //intersectionobserveroptions. root is highest element you want to compare to. threshhold is % visibility of element you're comparing to root. 'callback activated when 50% visibility.'
    let observerOptions = {
      root: null,
      rootMargin: "0px",
      threshold: [0.7]
    };

    //create new intersection observer
    let adObserver = new IntersectionObserver(
      this.intersectionCallback,
      observerOptions
    );

    //call it.
    adObserver.observe(adBox);
  };

  handleVisibilityChange = () => {
    if (document.hidden) {
      //changeclass to no animate through setstate
    } else {
      //changeclass to animate
    }
  };

  intersectionCallback = entries => {
    entries.forEach(entry => {
      let adBox = entry.target;

      if (entry.isIntersecting) {
        this.setState({ heroanim: "yes" });
      } else {
        console.log("not");

        this.setState({ heroanim: "no" });
      }
    });
  };

  render() {
    return (
      <div className="introCont">
        <div className="introFirstSection" />
        <div
          className={
            this.state.heroanim === "yes"
              ? "heroSection heroAnim"
              : "heroSection"
          }
          ref={this.heroRef}
        >
          <div className="heroGallery">
            <div className="slide-down">item 1</div>
            <div>item 2</div>
            <div>item 3</div>
            <div>item 4</div>
            <div>item 5</div>
            <div>item 6</div>
            <div>item 7</div>
            <div>item 8</div>
          </div>
        </div>
      </div>
    );
  }
}

export default Intro;