[JS/React] How to implement animation on scroll

May 26, 2021

programming
article cover

Scroll Listener vs Intersection Observer

To implement animation on scroll, we need to always keep watching where elements are at the moment.

The traditional way to do that is to set an event listener on scroll action and keep calculating where the element is. But there is another API, Intersection Observer, which has better performance, so we use it to animate.

How to use

The basic usage is quite simple, to create an “observer” and let it observe the elements.

const observer = new IntersectionObserver(callback, options);
const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach((entry) => {
      entry.isIntersecting // return boolean
    })
  }, 
  {
    // option
  }
)

observer.observe(targetElement)

We can take an object IntersectionObserverEntry as an argument of callback. And the property isIntersecting of the object can be used to detect the element is on the screen or not.

screen shot

Options

Options can also be set when we make the observer instance. threshold option is very useful to adjust how much of the intersection is enough to consider the elements to be on the screen right now.

threshold takes the value from 0 to 1.
1 means 100% intersection, in other words, only when the entire element appears on the screen the flag isIntersecting becomes true. And 0 is to mean that when the edge of an element touches the edge of the screen, the flag isIntersecting becomes true.
As shown in this diagram,

screen shot

On top of that, threshold can take an array as a value and it enables us to finely detect intersection step by step.

For instance, set threshold with an array of 100 steps ([0, 0.01, 0.02 … 0.99, 1]) and change the color of div.
We can use entry.intersectionRatio to know how much the element is intersecting on the screen.

const getThresholdArray = () => {
  const threshold = []
  
  for (let i=0; i<=1; i+=0.01) threshold.push(i)
  
  return threshold
}

const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach((entry) => {
      entry.target.style.backgroundColor = `rgba(22, 22, 22, ${entry.intersectionRatio})`
    })
  }, 
  {
    threshold: getThresholdArray()
  }
)

observer.observe(targetElement)

screen shot

Now we can see the color is changing depending on the percentage of intersection.

You can then add arbitrary animations at arbitrary timing.

Final code with React


Profile picture

Photographer / Web engineer working in Berlin.
A Japanese living with a Slovak wife and two German cats.