Do you like parallax or hate it, but it will not go anywhere. When used wisely, it can add depth and sophistication to a web application. The problem is that implementing parallax is productive – it’s not easy. In this article, we will discuss a solution that is both productive and, importantly, cross-browser.
Do not use scroll events or background-position to implement parallax.
For a better parallax effect, use CSS 3D transforms.
For Mobile Safari, use position: sticky to ensure that the parallax effect spreads.
If you need a turnkey solution, get Parallax helper JS from the UI Element Samples repository! You can watch the live demo.
To begin, consider two general ways to implement the parallax effect, in particular, why they do not suit us.
Bad: using scroll events
The key requirement of parallax is that it must be associated with scrolling the page; each change in the scroll position must correspond to a change in the position of the elements to which parallax is applied. It sounds simple, but an important mechanism of modern browsers is the ability to work asynchronously. This applies, in our case, to the scroll event. In most browsers, scroll event handlers are executed on the principle of “best possible” (in the original, “best-effort”, approx. Translator) and do not guarantee animation with every change in scroll position!
Bad: updating background-position
Another situation that we would like to avoid is rendering on each frame. Many solutions try to change the background position to provide a parallax effect, thereby causing the browser to redraw the affected parts of the page when scrolling. This solution can be expensive enough to slow down the animation significantly.
If we want to implement the parallax effect, we must apply the solution based on hardware accelerated properties (currently transform and opacity), which are independent of scroll events.
CSS in 3D
Both Scott Kellum and Keith Clark have done significant work in using CSS 3D to implement the parallax effect. The technique that they use effectively is as follows:
Set the container property overflow-y: scroll (and possibly overflow-x: hidden).
For the same element, apply the perspective value, and set the perspective-origin property to top left or 0 0.
For children, apply the Z-axis movement and scale them to provide a parallax effect without affecting their size on the screen.
The CSS for this looks like this:
perspective-origin: 0 0;
transform-origin: 0 0;
transform: translateZ (-2px) scale (3);
These rules can be applied to a piece of code:
<div class = “container”>
<div class = “parallax-child”> </div>
Scale adjustment for perspective
Moving an element to the background makes it smaller in proportion to the perspective value. You can calculate how much you need to scale the element using the formula: (perspective – distance) / perspective. Since we want the element to be parallax but retain its original size, we must scale it this way.
In the case shown above, the perspective is 1px and the distance Z for parallax-child is −2px. This means that the item must be increased to 3x. This you can see in the code: scale (3).
For any element that does not have a translateZ value, you can replace the distance value with zero. This means that the scale will be (perspective – 0) / perspective, i.e. 1. This item does not need to be scaled up or down. Very comfortable, really.
How does this approach work?
It is important to understand why this approach works, as soon we will use this knowledge. Scrolling is actually a conversion, so it can be hardware accelerated; this is mainly due to moving layers using the GPU. In normal scrolling without perspective, scrolling occurs in a 1: 1 ratio if we compare the scrolling of the container and its children. If you scroll down an element 300px, the elements will also move 300px.