Case study: create a parallax effect directly on <img> tags with JavaScript

Geoffrey Signorato
ITNEXT
Published in
4 min readMay 28, 2019

--

Parallax effects in web work only with the CSS background-image property, why is that? This is not really practical.

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

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

The fact that I couldn’t find this kind of library/plugin anywhere was surprising, so I decided to create a new one.

Main objective

I wanted to be able to add parallax effects without any change on HTML or CSS. The reason being I already had a website almost finished, and I didn’t see myself changing all my <img> tags to <div> with background-image.

This was the main thread of this library, I wanted to have a very simple way to apply parallax on any website already on production without any rework. And as a bonus, a very smooth and natural animation feeling — the effect should only give a plus and not cost anything else.

1st Issue: How not to break the layout?

How not to break the layout?

The first concern was to manage the transition of the image without breaking the layout. Usual parallax effects are located in a very specific area created for that purpose only, the main thread of this case is the opposite.

Parallax should be easily added anywhere there is an image, even if its located between two text blocs. And you don’t want the image to be transitioned anywhere on the website and potentially overlap with content.

Solution

I have therefore reached the conclusion to dynamically add a container as a parent of the image. 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 breaking 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 on the image. Meaning the image will have more matter to be transitioned with. This range can be easily calculated by:

(imageHeight * scale - imageHeight) = range

For example, if the image is 500px 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 comparing to the viewport, using a more laborious calculation:

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

And finally transcript this percentage into the range:

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

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

Cons: quality of the image

Theoretically, we can apprehend the fact that if the scale is applied to an image, we will lose quality.

In practice, this is hardly noticeable if the scale is set at 1.3 (which is the default value of the library). And even less if you cater for this by adding an image with a bigger size — meaning if your image is 500px and you want to apply a 1.5 scale, compensate by using a 750px width image.

Final render

Final render

Performances

With parallax animations, comes performance warning. Thanks to Paul Irish and html5rocks, a lot of answers were already provided and explained. Still, there is a lot to do here, some examples:

  • The scroll event is performance greedy, so the use of Request Animation Frame is highly recommended. I shall not elaborate, as this subject has already been touched upon by others.
  • Intersection Observer API is really powerful and performance light to check which elements are visible in the viewport. There is no need to cater for images that are not in the current viewport.
  • A key point is to reduce the reflow of the browser. One (among others) solution is to reduce as much as possible the fetching of the viewport and element offsets.
  • CSS Hardware Acceleration isn’t very well-know but yet powerful. Changing the transform: translateX(); to transform: translate3D(); will leverage the GPU power and offer better performances.

Performances are constant challenges against what has already been done. There is a permanent need to improve what can be improved particularly through new technologies or wrong initial implementation.

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

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

--

--