Case Study: Creating a Parallax Effect Directly on <img> Tags with JavaScript

Geoffrey Signorato
ITNEXT
Published in
4 min readMay 28, 2019

Why do parallax effects in web design only work with the CSS background-image property? This isn’t really practical.

This was my starting point. I wanted to apply a nice parallax effect directly to image tags for several reasons:

  • It’s the most natural way to use images on the web
  • The background-image property doesn’t easily support the equivalent of the picture tag, srcset, and sizes attributes
  • Using background-image with a CMS is not optimal

The fact that I couldn’t find a library or plugin that offered this functionality was surprising, so I decided to create one.

Main objective

I wanted to add parallax effects without making any changes to the HTML or CSS. This was because I already had an almost finished website, and I didn’t envision myself altering all my <img> tags to <div> with background-image.

This was the primary focus of this library. I wanted a simple way to apply parallax to any website already in production without any rework. As a bonus, I wanted a very smooth and natural animation feeling — the effect should only enhance, not detract from anything else.

1st Issue: How to avoid breaking the layout?

How can we prevent the layout from breaking?

The primary concern was managing the transition of the image without disrupting the layout. Typically, parallax effects are confined to a specific area created solely for that purpose. However, in this case, the main thread is the opposite.

Parallax should be easily added wherever there is an image, even if it’s located between two text blocks. We don’t want the image to transition anywhere on the website and potentially overlap with content.

Solution

I have thus concluded that dynamically adding a container as a parent of the image is the best approach. This container will have the same dimensions as the image and a hidden overflow. Now, the image can translate from an infinite number of pixels without disrupting the layout.

<img src="image.jpg" alt="image" />

will become:

<div style="overflow: hidden">
<img src="image.jpg" alt="image" />
</div>

2nd Issue: How to avoid blank spaces?

How to avoid blank spaces?

A new problem appeared: blank spaces when the image reaches its physical limit.

This was very problematic, considering that the whole point was to leave the initial layout of the page unaltered. Not to mention the smooth and natural animation initially planned.

Solution

I have opted for this solution: add a scaling transformation to the image. This means the image will have more material to transition with. This range can be easily calculated by:

(imageHeight * scale - imageHeight) = range

For example, if the image is 500px in height, and we apply a 1.5 scale, that means the image will have a 250px range to translate on.

Now we need to get the percentage of the image position compared to the viewport, using a more laborious calculation:

((viewportBottom - imageTop) / ((viewportHeight + imageHeight) / 100)) = percentage

And finally, translate this percentage into the range:

((percentage / 100) * range - range / 2) = translation

So the translation can be applied gradually to the image using the transform: translate(translation); property.

Cons: quality of the image

Theoretically, it’s understood that applying a scale to an image can result in a loss of quality.

In practice, this is hardly noticeable if the scale is set at 1.3 (which is the default value of the library). This effect is even less noticeable if you compensate for it by using a larger image. For instance, if your image is 500px and you want to apply a 1.5 scale, compensate by using an image with a width of 750px.

Final render

Final render

Performances

With parallax animations comes a performance warning. Thanks to Paul Irish and html5rocks, many answers have already been provided and explained. However, there is still a lot to do here, for example:

  • The scroll event is performance-intensive, so the use of Request Animation Frame is highly recommended. I won’t elaborate on this, as this topic has already been covered by others.
  • The Intersection Observer API is very powerful and light on performance when checking which elements are visible in the viewport. There’s no need to cater for images that aren’t in the current viewport.
  • A key point is to reduce the reflow of the browser. One solution (among others) is to minimize the fetching of viewport and element offsets as much as possible.
  • CSS Hardware Acceleration isn’t very well-known but is very powerful. Changing transform: translateX(); to transform: translate3D(); will leverage the GPU power and offer better performance.

Performance is a constant challenge against what has already been done. There is a continuous need to improve what can be improved, particularly through new technologies or correcting initial implementation errors.

This case study and this library only reflect my point of view. You are most welcome to challenge, debate, or argue any of the points I’ve made above.

Last but not least, you can check out the simpleParallax library on simpleparallax.com and github.

--

--