So the other day, I decided to redesign my website, and I faced a problem:
"How do I make it look a little bit more artistic?"
As you may already know, I'm a full-stack developer, also known as a "backend-developer-who-knows-some-front-end." That means I can code front-end stuff, but it's not my sweet spot.
So I experienced some trouble generating ideas for cool components.
That's why I set a goal for myself to start creating as many of them as possible, and this way to improve my UX skills.
That's how I've come up with this sleek parallax effect here:
I hope you like it because I will tell you how to create it yourself without adding dependencies.
It's a really simple effect, and we will use just React.js & CSS.
You can do it in many ways, so I won't claim I'm showing you the "best one" or even the "proper one." It's just my way of achieving the result.
Let's start with creating two DIVs:
import React from 'react';
import './Tutorial.scss';
export default function Tutorial () {
return (
<>
<div className="div1"></div>
<div className="div2"></div>
</>
);
}
I make them both have the screen size and let's apply some other styles to see what happens when scrolling.
.div1 {
min-height: 100vh;
width: 100%;
background: #000 url("./assets/hero-shot.jpg");
}
.div2 {
min-height: 100vh;
width: 100%;
background: #1680A2FF;
}
As you can see, I color the first DIV in black and set a cool background image. The second one I color in blue.
This is how the page looks now and what happens when scrolling down:
Ok. It looks and behaves normally…
If we wanted the background to have a fixed position, this could be achieved easily with some play with the style properties. But our goal for the parallax effect is to impact the whole section with all its child elements.
Let's add a snippet of text to the first div:
import React from 'react';
import './Tutorial.scss';
export default function Tutorial () {
return (
<>
<div className="div1">
<div>
<h1>Awesome React: The Story of A Sleek Parallax Effect</h1>
<p>How to make your website appear more artistic than usual with the help of some CSS and React.js</p>
</div>
</div>
<div className="div2"></div>
</>
);
}
.div1 {
min-height: 100vh;
width: 100%;
background: #000 url("./assets/hero-shot.jpg");
color: #ffffff;
display: flex;
align-items: center;
justify-content: end;
& > div {
width: 50%;
padding: 0 6%;
}
}
Here I make the .div1 to be displayed as a flex-box because it allows me to align the child components better. Also, you probably notice that I'm using non-system fonts for the title and body text. The title's font is "Oswald," and the body text's font is "Noto Sans."
However, those are not particularly important details.
I will do the most important thing right now and make the scrolling look badass, like different screens that move one on top of the other.
I will make both of the DIVs have relative positions so I can adjust programmatically their bottom property.
position: relative;
Now let's create a reference of .div1 and add a one-time side effect like this one:
import React, {useEffect, useRef} from 'react';
import './Tutorial.scss';
export const toPixels = (n: number) => `${Math.floor(n)}px`;
export default function Tutorial () {
const ref = useRef<HTMLDivElement|null>(null);
useEffect(() => {
if (ref.current) {
const {top} = ref.current?.getBoundingClientRect();
const height = ref.current?.clientHeight;
addEventListener('scroll', (ev) => {
if (ref.current) {
const calculatedTop = top + height*0.3;
let bottom = calculatedTop - window.scrollY;
if (window.scrollY > calculatedTop && window.scrollY < calculatedTop + height) {
ref.current.style.bottom = toPixels(bottom)
} else if (window.scrollY < calculatedTop) {
ref.current.style.bottom = '0';
}
}
})
}
}, []);
return (
<>
<div ref={ref} className="div1">
<div>
<h1>Awesome React: The Story of A Sleek Parallax Effect</h1>
<p>How to make your website appear more artistic than usual with the help of some CSS and React.js</p>
</div>
</div>
<div className="div2"></div>
</>
);
}
What do I do here?
First, I create an HTMLDIVElement reference that allows me to manipulate .div1 directly. It's not the most elegant move, but it will do the job.
Next, I create an event listener that listens for scrolling. I want some scroll events to move up or down .div1. For this purpose, I get the DIV's top
and height
properties. A few lines below, I use them for both how much to move down the DIV and when to move it.
As you can see, I use a constant calculatedTop
…
Its role is to be a threshold. If I don't reduce the height, its value will equal the bottom
property; thus, there will be no parallax effect because the whole element will move in parallel with the scrolling.
But if we reduce the height, we move up the threshold line a bit, and the element starts going down earlier than usual.
Now, it already looks cool with two sections, but what happens when I add a third one?
I must create a new reference and duplicate the effect hook. That's not a best practice. Instead, I will create a new component just for the parallax effect, and every time I want to apply it, I will wrap the target container.
import React, {PropsWithChildren, useEffect, useRef} from 'react';
import './SimpleParallax';
export const toPixels = (n: number) => `${Math.floor(n)}px`;
export default function SimpleParallax({children}: PropsWithChildren<{}>) {
const ref = useRef<HTMLDivElement|null>(null);
useEffect(() => {
if (ref.current) {
const {top} = ref.current?.getBoundingClientRect();
const height = ref.current?.clientHeight;
addEventListener('scroll', (ev) => {
if (ref.current) {
const calculatedTop = top + height*0.3;
let bottom = calculatedTop - window.scrollY;
if (window.scrollY > calculatedTop && window.scrollY < calculatedTop + height) {
ref.current.style.bottom = toPixels(bottom)
} else if (window.scrollY < calculatedTop) {
ref.current.style.bottom = '0';
}
}
})
}
}, []);
return (<div className="simple-parallax" ref={ref}>
{children}
</div>);
}
.simple-parallax {
position: relative;
width: 100%;
}
import React, {useEffect, useRef} from 'react';
import './Tutorial.scss';
import SimpleParallax from './SimpleParallax';
export const toPixels = (n: number) => `${Math.floor(n)}px`;
export default function Tutorial () {
return (
<>
<SimpleParallax>
<div className="div1">
<div>
<h1>Awesome React: The Story of A Sleek Parallax Effect</h1>
<p>How to make your website appear more artistic than usual with the help of some CSS and React.js</p>
</div>
</div>
</SimpleParallax>
<SimpleParallax>
<div className="div2"></div>
</SimpleParallax>
<div className="div3"></div>
</>
);
}
Now everything works fine!
It’s not “production-ready”, but you can clearly see the parallax effect. If you add it to a more finalized page with different sections and lots of content, then it will really look nice (like the main demo in the beginning. )
But it's not responsive.
I will end this article here because it becomes rather long, and I'm confident you can make it responsive on your own.
Anyway, suppose you like this article and are curious to discover how I made it responsive. In that case, you can take a look at the landing page’s source files here.
Thanks for your support!
Be happy, Sashe