React Parallax Scrolling Web Design Solution
Tags: react, javascript, webdev, ux
Published: 2021-03-01 15:08:07

### <center>React Parallax Scrolling Web Design Solution</center>

------------

#### <center>Browse our [Teachable](http://anobject.teachable.com "Teachable") courses.</center>

------------

![React Parallax Scrolling Web Design](https://user-images.githubusercontent.com/58220766/109092653-ee71f980-76e4-11eb-91b2-61d7f217acec.png)

### The general solution we're working with is this:

We are going to create an **HOC** (Higher Order Component).

It's a function that's going to take a **JSX Element**.

ex. **higherOrderFunction(\<div>\</div>)**

It's then going to return a **JSX Component**.

ex. **ParallaxComponent**

We'll then render it to our page like this:

<ParallaxComponent />

<br />

#### The logic for parallaxing is this:

We are going to **fix** a **JSX element** to our page (***in this case a DIV shaped like a circle***).

When we scroll our page **DOWN**, we'll manually scroll the **JSX element** **UP**.

Since we are manually scrolling our element up, we can control how fast or slow it moves.

This creates our **parallax effect**.

--------------

### Let's get our JSX on screen.

**A simple 'div'**.

```
return (
    <div>
        <img src="./back.png" alt="" />
        <div style={styles.obj}></div>
    </div>
);
```

**We'll style it as a circle**.

```
obj: {
    margin: 0,
    padding: 0,
    height: '200px',
    width: '200px',
    position: 'fixed',
    top: '50%',
    left: '50%',
    transform: 'translate(-50%, -50%)',
    borderRadius: '50%',
    backgroundColor: '#0083FF',
    boxShadow: '0px 0px 20px 10px #0083FF'
}
```

<br />

![parallax-not-complete](https://user-images.githubusercontent.com/58220766/103606485-83762480-4ee4-11eb-8872-e6bb37c6b936.png)

<br />

------------

### Let's write our Higher-Order Component.

#### We'll need 6 variables to accomplish our parallax animation.

1. **JSXElement** - the JSX element we pass into our HOC
2. **start_position** - where does this element start (***y position***) relative to the top of the document/page
3. **ease** - we control how fast or slow the JSX element scrolls relative to our page
4. **last_offset** - we keep track of how far, in total, we have scrolled up or down
5. **animation_running** - we use this to get our **requestAnimationFrame() loop** to start and stop

--------------------

### 5 main steps.

1. Create a **ref**.
```
const ref = React.createRef();
```

2. Clone the **JSX Element** while adding that **ref** into our clone.
```
const JSXElementWithRef = React.cloneElement(
    JSXElement,
    { ...JSXElement.props, ref: ref },
)
```

3. Save the new **JSX Component** in the state of our class component.
```
this.setState({
    ease: _weight,
    WrappedJSXElement: JSXElementWithRef
}, () => {
    this.setState({ start_position: ref.current.offsetTop }, () => {
        this.wrappedJSXElement = ref;
        document.addEventListener("scroll", this.animate_scroll);
    });
});
```

4. Render our new Component **in the HOC file**.
```
render() {
    return (
        <Fragment>
            {this.state.WrappedJSXElement}
        </Fragment>
    )
}
```

5. Build the logic for our animation loop.

**Every time we scroll our page, we want to scroll our JSX element(s).**

If we scroll 100 clicks of our wheel, we want to make sure we **put in a request to scroll our JSX element(s) 100 times as well**.

```
animate_scroll = () => {
    if (!this.animation_running) {
        this.animation_running = true;
        requestAnimationFrame(this.animation_loop);
    }
}
```

**The actual animation loop...**

```
animation_loop = () => {
    let current_offset = window.pageYOffset;

    let difference = current_offset - this.last_offset;
    difference *= this.state.ease;

    if (Math.abs(difference) < 0.05) {
        this.last_offset = current_offset;
        this.animation_running = false;
        return;
    }

    this.wrappedJSXElement.current.style.top = `${this.state.start_position - this.last_offset}px`;

    this.last_offset += difference;

    requestAnimationFrame(this.animation_loop);
}
```

**We do 4 major things here (not in this order).**

1. Calculate the **difference** between the current position of our document and the top of our page/document.

**Where our page starts.**

![initial_pageOffset](https://user-images.githubusercontent.com/58220766/103390897-c8651a00-4ae4-11eb-877c-1dc7cd163eb2.png)

**How we calculate the difference.**

![difference_calculation_example](https://user-images.githubusercontent.com/58220766/103390909-ddda4400-4ae4-11eb-9d00-c43790373b45.png)

2. Move our **JSX element** by that **difference**. (***the difference is multiplied by our **ease** to create our parallax effect ***)
3. Request another loop for our animation.
4. Our **exit clause** for the animation loop is if the **difference** is less than **0.05**. Basically if the **JSX element** has reached its destination.

<br />

![parallax-complete](https://user-images.githubusercontent.com/58220766/103606363-31350380-4ee4-11eb-95e9-c65b314b1037.gif)

------------

### We can use this logic for any type of JSX element.

#### DIVs, paragraphs, spans, images...

You can get the source files [here](https://github.com/an-object-is-a/reactjs-parallax-solution "here").

------------

#### <center>If you want a more in-depth guide, check out my full video tutorial on YouTube, **[An Object Is A](https://www.youtube.com/c/anobjectisa "An Object Is A")**.</center>

### <center>React Parallax Scrolling Web Design Solution</center>

{% youtube mehs92oAuYE %}