DEV Community

loading...
Cover image for React Parallax Scrolling Web Design Solution

React Parallax Scrolling Web Design Solution

anobjectisa profile image an-object-is-a ・3 min read

React Parallax Scrolling Web Design Solution


Browse our Teachable courses.


React Parallax Scrolling Web Design

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:

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>
);
Enter fullscreen mode Exit fullscreen mode

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'
}
Enter fullscreen mode Exit fullscreen mode

parallax-not-complete


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();
Enter fullscreen mode Exit fullscreen mode
  1. Clone the JSX Element while adding that ref into our clone.
const JSXElementWithRef = React.cloneElement(
    JSXElement,
    { ...JSXElement.props, ref: ref },
)
Enter fullscreen mode Exit fullscreen mode
  1. 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);
    });
});
Enter fullscreen mode Exit fullscreen mode
  1. Render our new Component in the HOC file.
render() {
    return (
        <Fragment>
            {this.state.WrappedJSXElement}
        </Fragment>
    )
}
Enter fullscreen mode Exit fullscreen mode
  1. 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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

How we calculate the difference.

difference_calculation_example

  1. Move our JSX element by that difference. (*the difference is multiplied by our **ease to create our parallax effect ***)
  2. Request another loop for our animation.
  3. 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.

parallax-complete


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

DIVs, paragraphs, spans, images...

You can get the source files here.


If you want a more in-depth guide, check out my full video tutorial on YouTube, An Object Is A.

React Parallax Scrolling Web Design Solution

Discussion (0)

pic
Editor guide