Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing styles when scrolling React

Tags:

scroll

reactjs

I want to add the scrolling effect. At the start, the elements have the opacity: 0.2 property. When element is reached in the browser window, it is to replace the property with opacity: 1. At this moment, when I scroll, all elements change the property to opacity: 1. How to make this value when element is reached in the browser, the rest of the elements have the property opacity: 0.2

class QuestionListItem extends Component {
  constructor() {
    super();
    this.state = {
      opacity: 0.2,
    };
  }

  componentDidMount = () => {
    window.addEventListener('scroll', () => {
      this.setState({
        opacity: 1,
      });
    });
  };

  render() {
    const { question } = this.props;
    const { opacity } = this.state;
    return (
      <div>
        <li
          key={question.id}
          className="Test__questions-item"
          style={{ opacity: `${opacity}` }}
          ref={
            (listener) => { this.listener = listener; }
          }
        >
          <p>
            {question.question}
          </p>
          <QuestionAnswerForm />
        </li>
      </div>
    );
  }
}

I want effect like this https://anemone.typeform.com/to/jgsLNG

like image 642
Elder Avatar asked Sep 13 '25 03:09

Elder


1 Answers

A proper solution could look like this. Of course, this is just a concept. You can fine-tune the activation/deactivation logic using props from getBoundingClientRect other than top (e.g. height, bottom etc). Important that you should not set the component's state on every single scroll event.

const activeFromPx = 20;
const activeToPx = 100;

class ScrollItem extends React.Component {
  state = {
    isActive: false
  }
  
  componentDidMount = () => {
    window.addEventListener('scroll', this.handleScroll);
    this.handleScroll();
  };
  
  handleScroll = () => {
    const { top } = this.wrapRef.getBoundingClientRect();
    if (top > activeFromPx && top < activeToPx && !this.state.isActive) {
      this.setState({ isActive: true });
    }
    if ((top <= activeFromPx || top >= activeToPx) && this.state.isActive) {
      this.setState({ isActive: false });
    }
  }
  
  setWrapRef = ref => {
    this.wrapRef = ref;
  }
  
  render() {
    const { isActive } = this.state;
    return (
      <div
        className={`scroll-item ${isActive && 'scroll-item--active'}`}
        ref={this.setWrapRef}
        >
        {this.props.children}
      </div>
    )
  }
}


class ScrollExample extends React.Component {  
  render() {
    return (
      <div className="scroll-wrap">
        <ScrollItem>foo</ScrollItem>
        <ScrollItem>bar</ScrollItem>
        <ScrollItem>eh</ScrollItem>
      </div>);
  }
}

ReactDOM.render(<ScrollExample />, document.getElementById('root'))
.scroll-wrap {
  height: 300vh;
  background: lightgray;
  padding-top: 55px;
}

.scroll-item {
  height: 60vh;
  background: lightcyan;
  margin: 10px;
  opacity: 0.2;
}

.scroll-item--active {
  opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
like image 57
gazdagergo Avatar answered Sep 16 '25 01:09

gazdagergo