DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,274 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Geoffrey
Geoffrey

Posted on

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

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 to not 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?

So 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

Performances

With parallax animations, comes performance warning. Thanks to Paul
Irish
and html5rocks, a lot of answers were already provided and explained to an extent. 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. Because 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 as much as possible, one (of many) solution to this is to reduce as much as possible to fetch 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 offers better performance.

Performances are constant challenges against what has been done. And improve
what can be improved thanks to new technologies or wrong initial implementation.


This case study and this library only reflect my point of view, which may
not be the best. 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.

Top comments (0)

In defense of the modern web

I expect I'll annoy everyone with this post: the anti-JavaScript crusaders, justly aghast at how much of the stuff we slather onto modern websites; the people arguing the web is a broken platform for interactive applications anyway and we should start over;

React users; the old guard with their artisanal JS and hand authored HTML; and Tom MacWright, someone I've admired from afar since I first became aware of his work on Mapbox many years ago. But I guess that's the price of having opinions.