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**.
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.
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.
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" />
<div style="overflow: hidden">
<img src="image.jpg" alt="image" />
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.
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.
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.
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.