In this new tutorial, we’ll start with some grayscale images and learn how to smoothly reveal their colored variants on scroll. To achieve the desired effect, we’re going to take advantage of different modern front-end features like CSS Grid, the
clip-path property, and the Intersection Observer API.
Our Grayscale to Color Effect
Without further intro, let’s check out what we’ll be building:
Although this effect has decent browser support, please note that it won’t work in some browsers like the earlier versions of Microsoft Edge. For this example, I’ll concentrate on the technique behind the effect without providing fallbacks for other browsers.
1. Begin With the HTML Markup
We’ll start with four sections:
Inside each section, we’ll put a heading and a fullscreen
div wrapper. The wrapper will include two empty
divs. Both elements will share the same background image. The first one will show the grayscale version of it, while the last one its original colored version:
The colored image will appear with a slide animation from left to right. Happily enough, we can customize the direction of this animation via the
data-animation attribute. All it needs is to add this attribute to the corresponding
.colored element with values
2. Define the Styles
With the markup ready, we’ll continue with the main styles.
For this demonstration, we’ll first define two utility classes which we’ll attach to the target elements:
By default, the
divs which hold the images will be stacked on top of another. Only the
div with the grayscale image will be visible.
To stack those elements, we’ll take advantage of a CSS Grid technique that I’ve covered in previous tutorials.
Here are the required styles:
To create the grayscale images, we’ll use the
grayscale() CSS function and pass the value of
100% as its argument. Also, we’ll give all grayscale images a background color as a fallback until each of those images loads:
As mentioned above, the colored images will initially be hidden. They will become visible with a slide animation as soon as a part of them becomes visible on the page.
Check out a GIF version of the final effect here.
To visually hide them, we won’t use any of the traditional CSS ways like
opacity: 0, and
transform: translateX(-100%). In fact, we’re going to try
clip-path, a modern CSS property that can help us build interesting effects.
clip-path Property Explanation
clip-path property gives us the ability to cut away parts of an element and show only a specific portion of it. The visible area can be represented with different shapes (circles, ellipses, polygons, rectangles).
In our example, we’ll use the inset() function to build the desired rectangle.
In its simplest form, it can receive up to four values in a clockwise direction which specify the side offsets (top, right, bottom, left) that generate the selected area. For simplicity, we can use the margin shorthand which gives us the ability to set all four insets with one, two, three, or four values. Optionally, we can pass some extra values to this function for specifying the rectangle’s roundness.
So, just for practice, let’s assume that we have the following Pixabay image which is 200px by 300px.
If we give it
clip-path: inset(10px 20px 30px 40px), the resulting image will be 140px by 260px:
Going a step further, an element with
clip-path: inset(0) means that the whole element will appear.
On the other hand, an element with one of the four values set to 100% means that it will be squeezed and hidden. Keep in mind that the order of this value inside the function matters and can produce different animations.
Back to our example, here’s how we initially hide the colored images:
3. Animate on Scroll
The colored images will be animated and toggled on scroll.
To accomplish this task, we’ll take advantage of the Intersection Observer API.
When at least 50% of each target element enters the viewport, it will receive the
is-animated class. Otherwise, it will lose this class and become hidden.
Note1: To see how this API works and what it returns as you scroll, print in the browser console the
Note2: Initially, instead of the
intersectionRatio property I tried the
isIntersecting one. However, I noticed that it’s buggy in Firefox.
Tip: If you prefer the animation to run only once, you have to call the
unobserve() method, like this:
And the associated CSS class:
Obviously, like any modern tool, this effect has some limitations regarding browser support, especially if you target older browsers. For example, as a fallback, you can show the colored images by default in case the browser doesn’t support the
Hopefully though, this exercise helped you learn something new and has given you some creative ideas. Here’s a reminder of what we built:
As always, thanks a lot for reading!
Next Level Front-End Learning
When you’re learning anything to do with front-end, the key is practice!