0

I am building a landing page with a lot of animations. When I need to animate something I create an event listener to the scroll and attach onScroll function, which triggers my animations by setting state.

I currently create less than 10 listeners with 10 functions to check the state.

I would like to create an entity that uses 1 event listener and then all my landing page components should subscribe and get position.

How can I do this? I am using ES6 and React.

A bit of my code:

  componentDidMount() {
    this.toggleAppBar();
    addScrollEventListener(this.handleOnTopScroll);
    addScrollEventListener(this.handleFirstScroll);
  }

  handleOnTopScroll = () => {
    let scrollTop = document.body.scrollTop
    if (scrollTop === 0) {
      this.toggleAppBar();
    }
  }

  handleFirstScroll = () => {
    let scrollTop = document.body.scrollTop
    //console.log(scroll);
    if (!scroll) {
      this.toggleAppBar();
      scroll = true
      return
    }
    if (scrollTop === 0) {
      scroll = false
    }
  }

Solution works as it is required but I would need to add a bunch more listeners for other components

12
  • You can use scroll() events. Ref: api.jquery.com/scroll Commented Jul 19, 2017 at 13:05
  • 1
    sorry I dont use jQuery I use only vanilla Commented Jul 19, 2017 at 13:08
  • 1
    You could use window.onscroll event. developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/… Commented Jul 19, 2017 at 13:18
  • 1
    React with better performance than vanilla JS? I doubt it. Commented Jul 19, 2017 at 14:56
  • 1
    You could do that with one event listener, and then call two different functions, one to handle the scroll and other to check if the user has gone back to position zero. Btw, you should probably raise a different question for your performance issues with some sample code. Commented Jul 19, 2017 at 15:55

2 Answers 2

1

We do something similar on our company website, with somewhat complex animations on scroll. The solution we came up with was to normalize the scroll on a parent component so that 0 scroll = 0 and fully scrolled would equal 1. We passed the "animationProgress" to child components which could determine how and when to animate.

To do this we use the following:

calculateAnimationProgress (target) {
  return mapRange(target.scrollTop, 0, target.scrollHeight - window.innerHeight, 0, 1);
}

//Utility function to map a value to it's corelated value within a given range
const mapRange = (value, inMin, inMax, outMin, outMax) => (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;

That gives us a normalized scroll from 0 to 1, so that if the height changes you'll always have a known range, though you may need to re-calculate on window resize etc.

In your child components you can then tweak performance by only allowing them to re-render when they are within a given range of your "animationProgress".

shouldComponentUpdate (nextProps) {
  return isInRange(nextProps.animationProgress, 0, 1);
}

//Utility function returns boolean if value within a given range
const isInRange = (val, min, max) => {
  return val >= min && val <= max;
}

I hope that helps. You can check out our results from this approach at http://redshiftdigital.com

Sign up to request clarification or add additional context in comments.

4 Comments

Okay so as I understand you have a high level component that monitors the scroll and then pass that data to child components in which you set the triggers according to the value of scroll.
Yep, that's right. We ended up tying it all to a GSAP timelines in the components, which also uses 0 to 1 for it's animation progress. greensock.com/timelinemax .
Just two questions, do you wrap each individual component, let say section 1, section 2 ... with this higher order component, or you wrap the whole app itself and what technique do you youse to pass the actual value to children?
We have a higher order component wrapping, rather than the whole app. We felt we had more granular control over what specifically was rendering on each scroll tick, since there are a lot.
1

With plain JS, you could use element.onscroll event to listen to scroll events on specific element.

You may consider using jQuery's scroll() event, if jQuery is available.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.