Creating a parallax effect using react-spring
Tags: react, reactspring, animation, parallax
Published: 2019-12-09 18:56:17

# 

I can hear you think. "Another blog post using react-spring?! This guy..." Well, turns out I'm having quite a good time using react-spring for all kinds of animations/movements. Also, writing this down helps me better understand how it works. So tough luck, here goes nothing.

There are a couple of good libraries out there that can help you achieve a parallax effect quite easily. But the basics for a parallax effect are pretty simple: component X (or a part of it) moves with a different speed horizontally or vertically than component Y, which creates a sense of depth. So achieving the same without a plugin specifically for this effect is actually not that hard.

## The objectives

1. Attaching a scroll listener
3. Applying the parallax effect by setting the translateY property

### 1. Attaching a scroll listener

Attaching a scroll listener is actually really easy uing React's `useEffect` hook. We pass the hook a function that adds an event listener. This function has `scroll` as it's first argument, and a function `handleScroll` as second argument. We return a function which removes this event listener. By returning this function we're telling React to do some cleanup when the component is updated or unmounted.

```js
import React, { useEffect, useRef } from 'react';

const Comp = () => {
  const ref = useRef();

  const handleScroll = () => {
    const posY = ref.current.getBoundingClientRect().top;
    const offset = window.pageYOffset - posY;
    console.log(offset);
  };

  useEffect(() => {
    window.addEventListener('scroll', handleScroll);

    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  });

  return (<div ref={ref}>Contents of your component</div>)
}

export default Comp;
```

Notice that in the `handleScroll` method we're calculating the relative Y distance of our component by subtracting the `top` property of the bounding client rect from the current offset of the window. If you don't do this, the impact of your parallax effect will be based on where (vertically) your component is placed. By using this nifty correction, we'll make sure that our `offset` has a negative value as long as our component's top is below the viewport's top. When our component's top has passed the viewport's top, the value for `offset` becomes positive.

*Notice, no react-spring has been used yet ;-)*

### 2. Applying the parallax effect

Now that we have the relative Y position of our component, we can start using this to create the parallax effect. We'll use a basic spring for this and define the default offset (which is 0) using the `useSpring` method. This returns both the interpolated value and an update/set function. We'll use this update/set function in our `handleScroll` method.

**I've explained a bit more about the `useSpring` method in one of my previous posts, see [this link](https://medium.com/@robbertvancaem/getting-started-with-react-spring-bdfbf833d46) if you want to know more about it.*

```js
import React, { useEffect, useRef } from 'react';
import { useSpring } from 'react-spring';

const Comp = () => {
  const ref = useRef();
  const [{ offset }, set] = useSpring(() => ({ offset: 0 }));

  const handleScroll = () => {
    const posY = ref.current.getBoundingClientRect().top;
    const offset = window.pageYOffset - posY;
    set({ offset });
  };

  useEffect(() => {
    window.addEventListener('scroll', handleScroll);

    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  });

  return (<div ref={ref}>Contents of your component</div>)
}

export default Comp;
```

Right now, we have everything we need to enable our parallax effect. So the next step would be to start moving stuff around. For this example, we'll be using some 'dirty' inline styling, you could use something like `styled-components` or any other tool for this.

```js
import React, { useEffect, useRef } from 'react';
import { animated, useSpring } from 'react-spring';

// You can use this `calc` method to increase the impact
// of the effect by playing around with the values and units.
const calc = o => `translateY(${o * 0.1}px)`;

const Comp = () => {
  const ref = useRef();
  const [{ offset }, set] = useSpring(() => ({ offset: 0 }));

  const handleScroll = () => {
    const posY = ref.current.getBoundingClientRect().top;
    const offset = window.pageYOffset - posY;
    set({ offset });
  };

  useEffect(() => {
    window.addEventListener('scroll', handleScroll);

    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  });

  return (
    <div style={{
      background: '#123456',
      position: 'relative',
      width: '100vw',
      height: '400px',
      ref={ref}
    }}>
      <animated.div style={{
        background: '#654321',
        position: 'absolute',
        width: '100vw',
        height: '100px',
        transform: offset.interpolate(calc)
      }} />
    </div>
  )
}

export default Comp;
```

And that's it! As you can see all it takes is to define an `animated.div` with a style object. By interpolating the offset provided through `react-spring` with a function `calc` we have full control over the impact of the effect. You could for example change the `calc` function to manipulate the `translateX` property. This would make our parallax effect to act horizontally.

Check out the ugly but working CodeSandbox below
{% codesandbox o3933 %}

## Got questions or feedback?

Did you find this useful? Or do you know of a different cool way to achieve a parallax effect? I'm thinking of trying to find a nice way of defining different depths :thinking: If you have any different topics you'd like to hear about, let me know! Next topics I'll cover will probably be:
- Setting up and writing your first tests with Jest
- How to setup staging/production environments using Now

Don't forget to start following me here, on [Medium](https://medium.com/@robbertvancaem) or on [Twitter](https://twitter.com/robbertvancaem)!