Erik Lopez .net

Error: Unable to access Twitter at URL (http://www.twitter.com/statuses/user_timeline/erik_lopez.json?count=1). Verify service status. (HTTP code 401.)

Parallax navigation

My first major web design conference experience was last August at An Event Apart 2008 in San Francisco.  It was an awesome conference and if travel restrictions are lifted at my day job anytime soon I’m hoping to go back in ‘09.  During the conference, or more specifically, during Dan Cederholm’s presentation, Implementing Design: Bulletproof A-Z, many of us were first introduced to Clearleft’s Silverback application’s website and it’s clever use of CSS to mimic an animation technique called “parallax scrolling”.  Paul Annett, Clearleft’s designer, gives a great explanation of the effect and the underlying CSS over at Vitamin.

When I first saw the technique in action, I was blown away by both the simplicity of the technique, as well as the cleverness of it all.  Sometimes at the ol’ 9 to 5, I don’t think I’ll ever have opportunity, let alone the time, to devise and implement such innovation.  While the artistry is not lost on me, I felt the parallax effect could be used somewhere more visible to the masses in order to give a page’s design some sense of motion.  In order to do that, I wouldn’t be able to rely on CSS alone, so I turned to the all-powerful jQuery.

The images

The most important step to any parallax project is definitely the selection of the images.  Being a child of the 80’s with not much more than a Nintendo and big dreams, I had to go with a Super Mario Bros. scene.  Here are the three tranparent PNG images I used, starting with the “distant” sky and clouds, then moving to the “closer” hills and shrubs, and ending with the “nearer” ground, floating blocks and pipes.

cloudscene

middlescene

groundscene

The HTML

The HTML is straightforward.  I created a simple unordered list-based navigation with four items with the text of each navigation item being wrapped in a <span> tag.

1
2
3
4
5
6
<ul id="navigation">
    <li><a href="#1"><span>Home</span></a></li>
    <li><a href="#2"><span>About</span></a></li>
    <li><a href="#3"><span>Portfolio</span></a></li>
    <li><a href="#4"><span>Contact</span></a></li>
</ul>

The CSS

We begin first by styling the list and list-items.  Each list item will have the same dimensions as the images, 200 x 500, and since the list item is the most “distant” element in our pseudo-three-dimensional environment it will use the scene that occurs “furthest” from the viewer, the cloud scene.  All of the backgrounds are also repeated along the X-axis in order to give us infinite “scrollability” and animation time.

1
2
3
4
5
6
7
8
9
10
ul#navigation {
    margin: 0;
    padding: 0;
    list-style-type: none;
}
ul#navigation li {
    width: 200px;
    height: 50px;
    background: url('CloudScene.png') repeat-x;
}

Next we style the next-closest element in our pseudo-three-dimensional environment, the link.  We’ll set the link to fill the entire list item, set the attributes for the link text, and finally, set the link background to the next closest scene, the hills and shrubbery.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ul#navigation li a:link,
ul#navigation li a:visited
ul#navigation li a:hover,
ul#navigation li a:focus,
ul#navigation li a:active {
    display: block;
    width: 200px;
    height: 50px;
    color: #1F1F1F;
    font-size: 20px;
    font-weight: bold;
    text-decoration: none;
    background: url('MiddleScene.png') repeat-x;
}

The final step in implementing the CSS is to style the span element that is nested within the link.  Here, we position the text within the link using padding, and adjust the size of the span to compensate for the padding in order to prevent the span from overstepping its boundaries.  Lastly, we apply the scene closest to the viewer to the background of the span, the ground, blocks, and pipes scene.

1
2
3
4
5
6
7
8
ul#navigation li a span {
    display: block;
    padding: 22px 0 0 30px;
    /* Correct sizing due to text placement */
    width: 170px;
    height: 28px;
    background: url('GroundScene.png') repeat-x;
}

So far…

So far, all we have is a regular navigation block with what appears to be a single image in the background of each item.

navillax

The Javascript, or Why I Love jQuery

Here’s where the magic happens, the Javascript.  I probably wouldn’t have even attempted this concept if it wasn’t for jQuery.  If you’re not familiar with jQuery, head on over to the documentation and have a look around.  If you use much Javascript in your projects, I promise, it’ll make your life a whole lot easier.  Let’s take a look at the javascript block and break it down.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script type="text/javascript">
   $(document).ready(function(){
     $("#navigation > li > a > span")
       .css( {backgroundPosition: "0 0"} )
       .css( "cursor","pointer" )
       .mouseover(function(){
         $(this).stop().animate(
           {backgroundPosition:"-1000px 0"}, 5000);
         $("#navigation > li > a:contains('" + $(this).text() + "')").stop().animate(
           {backgroundPosition:"-500px 0"}, 5000);
         $("#navigation > li:contains('" + $(this).text() + "')").stop().animate(
           {backgroundPosition:"-200px 0"}, 5000);
       })
       .mouseout(function(){
         $(this).stop().animate(
           {backgroundPosition:"0px 0"}, 3000);
         $("#navigation > li > a:contains('" + $(this).text() + "')").stop().animate(
           {backgroundPosition:"0px 0"}, 3000);
         $("#navigation > li:contains('" + $(this).text() + "')").stop().animate(
           {backgroundPosition:"0px 0"}, 3000);
       })
  });
</script>

So we start by setting the span’s background-position CSS property to the coordinates 0,0 just to make sure that the background is where it should be.  I also set the CSS cursor property to pointer, just to make it clear that it’s a link in browsers that still display the default cursor when hovering over our navigation items… I’m looking at you, IE7.  Next, we add the mouse over behavior of our navigation items that animates the backgrounds of each of our layers.

It starts by stopping any animations that may currently be running.  The closest layer, the ground, blocks, and pipes scene then is animated by sliding the background image to the left by 1,000 pixels over a duration of five seconds (5,000 milliseconds).

The second animation that will be running simultaneously is the hills and shrubs scene, that is attached to the link tag.  We’ll use jQuery to select only the link tag that contains the text of the element that is currently in the mouseover state.  This prevents every middle layer throughout our navigation block from animating at the same time.  The background of this element uses the same duration as the top most layer, five seconds, but the background ends up only traveling a distance of 500 pixels, rather than 1,000 pixels, like the span element’s background.  This is where we get the parallax effect.  jQuery animates both layers at the same time, for the same duration, yet the layer “furthest” from the viewer will go more slowly than the the “closest” layer, because it is traveling less distance in the same amount of time.

Finally, we animate the list item layer, or the cloud scene.  Again, we use jQuery selectors to choose the correct list item, give the animation a duration of five seconds, and cause the background image to travel only 200 pixels.

To end the fun, once the mouse has left the navigation item, I’ve added a mouseout function that again ends any animation currently running on each of the navigation items and scrolls the background to it’s starting coordinates of 0,0 over three seconds.  Since they are all offset a different amount of pixels, scrolling back will also have the parallax effect.

Wrapping up

Here’s the final product:

Does anyone think this effect is useful or necessary?  Or does it just add page bloat that doesn’t serve any useful purpose?  I’m interested to hear what you think, as there was quite the heated discussion over at Paul Annett’s Vitamin article.  At any rate, I think I’ve accomplished my goal of bringing the parallax effect out of the “shadows”, so to speak.

Leave a Reply