In order to view this website properly and use all its capabilities, please activate JavaScript.
Here are the instructions how to enable JavaScript in your web browser.

Parallax scrolling: why a fixed background?

By Thibaut Gilbert
new

I recently decided to dive into parallax scrolling. Basically the proposed solutions can be splitted in two categories:

  • sites which implement an event listener on the window's scroll event (like the really nice Laura Marling's song website The beast)
  • sites which use a third party script, like Skrollr or Stellar

Although it's entirely to a developer credit to implement his own solution, in the case of parallax scrolling the use of an external library is advisable, for code maintainability, clarity and performance. Anyhow, whatever the category, they're graphically based on a succession of slides, each slide containing a fixed background image (via the background-attachment CSS property). Why so?

First I'll quickly recall the visual concepts of parallax, then I'll implement it with Skrollr and a touch of Scss. I choosed Skrollr because it doesn't have any dependency and offers a relative positioning mechanism out of the box (for Stellar you have to use a third party jQuery plugin like Waypoints).

Looking out of the train's window...

To my mind the definition of parallax scrolling is perspective in motion. Throughout most of the websites, the parallax effect is implemented by animating a serie of slides, by a manual or automatic scroll, like on VonDutch's website. Each slide's content appears in perspective and is gracefully in motion. But how to achieve that?

I can make a parallel with the passenger inside a moving train, looking at the window. The browser's window represents the train's window and each slide is a different type of landscape passing in front of the window. Moreover, from the passenger's referential, all elements in the landscape seem moving in an opposite direction of the train movement. Closest is the element, the faster is its apparent speed. Fairly simple.

Von Dutch website, a good example of parallax scrolling

Let's try without a fixed background

Each slide has a specific background. As its name implies the background is the farthest element in the slide, thus it will be the one with the lowest apparent speed.

Let's create a bunch of <section> elements to represent our slides. That will be all for the index.html file, phew... ;)

<main>
  <section class="slide slide-1">
  </section>

  <section class="slide slide-2">
  </section>
  <!-- and more ... -->
</main>

Now the Scss files: _variables.scss, _natural.scss and _skrollr.scss. These partials are included in the main.scss file.

In _natural.scss we put the basic style needed for each slide. A slide is 1000px high and occupies all window's width. So a constraint in parallax scrolling is that each slide's background must covers the entire window and always presents it's main content centered. This is achieved by setting the background-position property to 50% 0.

Concerning images' width, you either can take the media query approach or just stick to a unique image's width (in this case each image should gradient to a background-color).
For images' heights, simply pick the slides' one, ie 1000 pixels.

Now it's time to activate the parallax effect via the Skrollr library.

/* ---- main.scss ---- */
@import "variables";
@import "natural";
@import "skrollr";

 /* ---- _natural.scss ---- */
.slide {
  height: 1000px;
  background-repeat: no-repeat;
  background-position: 50% 0;
}

.slide-1 {
  /*each png has a transparent gradient on its left and right edges */
  background-image: url("../images/slide1-bkgd.png");
  background-color: navy;
}

.slide-2 {
  background-image: url("../images/slide2-bkgd.png");
  background-color: lime;
}

Based on the train metaphor, we have to move the background image of each slide in sync with the scroll event and in a reverse direction. This move has to be linear, which means that the background must already be in motion when its slide enters the viewport and must still be in motion when the slide leaves the viewport. To do so we animate the y axis of the background-position property.

As I mentioned it earlier, it's preferable to use Skrollr over a homemade solution:

  • the library takes care for us of checking the current scroll position against the offsetTop of each slides and offers us a neat relative positioning API
  • Skrollr doesn't trigger graphical changes each time a scroll event is fired. Instead it relies on requestAnimationFrame() and only flushes the latest changes, which reduces jank busting:
  • the skrollr-stylesheets extension give the possibility to put all the Skrollr instructions into stylesheets, which is a good point for separation of concerns

Include skrollr.stylesheets and skrollr scripts in index.html (the order is relevant because skrollr.stylesheets will create the data-* attributes used by skrollr) and simply init the library.

<script src="bower_components/skrollr-stylesheets/dist/skrollr.stylesheets.min.js"></script>
<script src="bower_components/skrollr/dist/skrollr.min.js"></script>
<script type="text/javascript">
  skrollr.init();
</script>

Declare a $background-offset variable and apply it to each background before and after the corresponding slide respectively enters and leaves the viewport. For that, Skrollr has defined a custom version of CSS animations, which is really handy.

The Skrollr keyframes could be read as follow: (viewport_anchor)-(element_anchor). For example bottom-top means when the bottom of the viewport and the top of the element are aligned, ie the element is about to enter the viewport.
Skrollr will calculate all the intermediate values for the y axis of background-position.

If you give the animation a try at this point, you'll notice that something smells bad.

/* ---- _variables.scss ---- */
$background-offset: 200px;

/* ---- _skrollr.scss ---- */
.slide {
  -skrollr-animation-name: background-offset;
}
@-skrollr-keyframes background-offset {
  bottom-top {
    /*positive offset, the slide is about to ENTER the viewport*/
    background-position: 50% $background-offset;
  }
  top-bottom {
    /*negative offset*/
    background-position: 50% -#{$background-offset};
  }
}

Oops, maybe a fixed background is a good idea

Let's recap. Our background image has the same height as its container, the slide. In its animation (considering a descending scroll) the background-image is first pushed down, then centered and finally pushed up, regards to its container. This means that on the few pixels where the slide enters then leaves the viewport, we don't see the background-image but the background of the slide element.

Fortunately some smart developer comes up with the background-attachment: fixed; trick. This way we force the background image to be always visible - as soon as its container appears in the viewport - while keeping the offset effect.

A nice side effect is this groovy Start Wars' wipe transition, because every slides act as masks on their backgrounds. However, the introduction of those fixed backgrounds leads to a continuous invalidation of the view composition. Therefore the whole viewport area is continuously repainted as soon as the scroll moves, which is clearly not optimal. On this topic, see the excellent post of Wills Bithrey (references bellow).

/* ---- _natural.scss ---- */
.slide {
    background-attachment: fixed;
}

Notes: Skrollr has added space at the bottom of the document, this is because he takes into account the offsets we specified. This is useful if you've set offsets on DOM elements, which are susceptible to go outside the Document. In our case, those offsets concern background-images so this extra space is useless.

skrollr.init({
  forceHeight: false
});

In some configuration you might notice that when the slide is nearly centered in the viewport, the background-attachment trick doesn't manage to compensate the offset. In this case, simply reducing the positive offset will do the trick.

@-skrollr-keyframes background-offset {
  bottom-top {
    /*positive offset*/
    background-position: 50% 100px;
  }
  top-bottom {
    /*negative offset, reduced*/
    background-position: 50% -200px;
  }
}

Throughout this post I tried to demonstrate why the background-attachment property, in conjunction with an animated background-image, is essential to create a realistic parallax effect.

However a nasty consequence is that a repaint event takes place, as long as the user scrolls. This event targets the entire viewport. I'm eager to hear some solutions addressing this issue. Please, comment heavily!

Latest See all →

Parallax scrolling: why a fixed background?
new

This post explains the use of a fixed background-image in parallax scrolling. To do so I review the visual concepts behind parallax and illustrate it with the Skrollr library and a few lines of Scss.

Building an audio player with CSS Flexbox and JavaScript Promises
new

A step by step tutorial to learn how to build an HTML5 audio player from scratch, focused on:

  • the use of CSS Flexbox for a simple responsive layout,
  • an implementation of the Mediator pattern in JavaScript to develop a maintainable player,
  • the use of Promises to simplify callbacks management

About

47tibo.com is a blog focused on JavaScript, HTML5 and Ruby on Rails, offering tutorials and best practices, from intermediate to advanced.
Posts are structured around code snippets and out of the box examples, featuring various topics, from abstract to concrete ones (like performance or HTML5 features).
Source codes are under Creative Commons licence but original graphics are copyrighted.

For a presentation of all techniques at play on 47tibo.com, click here.

Connect

Liked what you've seen here?
If so you might be interested in the following: