React Parallax Scrolling Web Design Solution
Add parallax elements to your next web design.
Browse our Teachable courses.
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 />
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’}
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.
How we calculate the difference.
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.
We can use this logic for any type of JSX element.
DIVs, paragraphs, spans, images…
You can get the source files here.
If you would like a more in-depth guide, check out my full video tutorial on YouTube, An Object Is A.
Be sure to follow us on Instagram and Twitter to keep up with our latest Web Development tutorials.