Skip to content
This repository has been archived by the owner on Jul 27, 2020. It is now read-only.
sreeramanmg edited this page May 7, 2012 · 7 revisions

jQuery Parallax Recreating the Nike Better World Parallax Effect Tutorial

A couple of months ago, I created a jQuery Vertical Parallax Demo that manipulated CSS to make multiple backgrounds move at different speeds relative to the users movement of the scroll bar. This type of effect is slowly appearing across various websites on the web, achieved using many different techniques. Nikebetterworld took the idea to a new level.

In today's tutorial, we're going to take the original jQuery Parallax script I wrote and recreate a webpage similar to Nikebetterworld.

If you'd like to see what we'll be creating, go check out the demo or download the files.

The HTML

Our page will consist of 6 sections: header, footer and 4 articles. On the right, we'll place an unordered list that links between the articles and remains fixed on the page so it doesn't move.

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript" src="scripts/nbw-parallax.js"></script>
<script type="text/javascript" src="scripts/jquery.localscroll-1.2.7-min.js"></script>
<script type="text/javascript" src="scripts/jquery.scrollTo-1.4.2-min.js"></script>
<script type="text/javascript" src="scripts/jquery.inview.js"></script>
<script type="text/javascript">
$(document).ready(function(){
    $('#nav').localScroll();
})
</script>

To start, at the top of the page, we'll reference all of the JavaScript files we'll use to make the effect work. The scripts we're using are:

<ul>
    <li><a title="jQuery 1.4.4" href="http://api.jquery.com/category/version/1.4.4/">jQuery 1.4.4</a></li>
    <li>The script I wrote (which we'll cover shortly)</li>
    <li><a title="jQuery localscroll" href="http://www.flesler.blogspot.com/2007/10/jquerylocalscroll-10.html">jQuery localscroll</a> (which smoothly scrolls between articles)</li>
    <li><a title="jQuery scrollTo" href="http://www.flesler.blogspot.com/2007/10/jqueryscrollto.html">jQuery scrollTo</a> (also needed for smooth scrolling)</li>
    <li><a title="jQuery In View" href="http://remysharp.com/2009/01/26/element-in-view-event-plugin/">jQuery Inview</a> (determines whether a particular article is in view)</li>
</ul>

We also need to initiate the localScroll function on the unordered list of article links that are fixed to the right of the page.

<ul id="nav">
    <li><a href="#intro" title="Next Section"><img src="images/dot.png" alt="Link" /></a></li>
    <li><a href="#second" title="Next Section"><img src="images/dot.png" alt="Link" /></a></li>
    <li><a href="#third" title="Next Section"><img src="images/dot.png" alt="Link" /></a></li>
    <li><a href="#fourth" title="Next Section"><img src="images/dot.png" alt="Link" /></a></li>
    <li><a href="#fifth" title="Next Section"><img src="images/dot.png" alt="Link" /></a></li>
</ul>

Inside of our body tag, we begin with the unordered list that provides a quick link to each section. Without JavaScript, these links will act as anchors that simply jump to what ever article the user clicks on. With the use of jQuery though, we've already set each link up to smoothly scroll to it's respective article. The smooth scrolling will help showcase the parallax effect we're about to set up.

<div id="intro">
    <div class="story">
        Article content here
    </div> <!--.story-->
</div> <!--#intro-->

Finally, for our HTML, we have the containers that make up each article.

The CSS

I won't cover what all of the CSS does, just that which is relevant to the working of the effect. Let's take a look at the CSS for the HTML container I just mentioned above:

#intro{
    background:url(images/firstBG.jpg) 50% 0 no-repeat fixed;
    color: white;
    height: 600px;
    margin: 0;
    padding: 160px 0 0 0;
}

The first article (#intro), will have one background image and a height of 600px. The width isn't defined so it will stretch to 100% of the window. Each background image used for the articles I have made 1900px so the majority of monitors will have a full image background. Anything over 1900px will see a white border.

.story{
    margin: 0 auto;
    min-width: 980px;
    width: 980px;
}

Inside of the #intro container, we have .story which is present in every article container. This purely keeps each article centered in the browser and with a width of 980px, almost every monitor will see the website without the need for horizontal scroll bars.

Multiple Backgrounds

Some of the articles have multiple backgrounds. Nikebetterworld achieved this using extra HTML in the article container which is exactly what I've done for the "Multiple Backgrounds" article, just like this:

<div id="second">
    <div class="story">
        <div class="bg"></div>
            Article content here
    </div> <!--.story-->
</div> <!--#second-->

The div with the class of "bg" has it's own background:

#second .bg{
    background: url(images/trainers.png) 50% 0 no-repeat fixed;
    ...
    position: absolute;
    ...
    z-index: 200;
}

This div is also given an absolute position and a high z-index to make sure it appears above everything else.

However, although this is the best way to achieve support across all browsers for multiple backgrounds, it's also possible to use CSS3 multiple backgrounds, which I demonstrate in the fourth article in the demo "Empty Containers vs CSS3 Multiple Backgrounds".

Using CSS3 means we don't have to place empty containers in the markup and leave the styling to CSS which is how things should (and one day will) be. So, let's look at the CSS for the fourth article container:

#fourth{
    background: url(images/bubbles2.png), url(images/bubbles2.png), url(images/bubbles1.png), url(images/fourthBG.jpg);
    background-position: 50% 0, 50% 0, 50% 0, 50% 0;
    background-color: #036;
    background-attachment: fixed;
    background-repeat: repeat, repeat, repeat ,no-repeat;
}

As you can see, I've used CSS3 to give one element multiple backgrounds. I've also given each of those backgrounds their own position values. We'll be manipulating these values in jQuery shortly.

Note: In my original jQuery Parallax Demo, I used shorthand CSS to define all of the backgrounds values which is what I did in this demo to begin with too. However, because I used shorthand, when I started manipulating the background positions via jQuery, it meant not only were the background positions being changed, but so too were the background urls and this made the jQuery very heavy. Infact, only Chrome is able to cope with running that original script smoothly at this time, but I do plan to take what I've learnt in this demo and optimize the code in the hope it will run smoothly across all browsers too.

So, it is entirely up to you which method you use to create multiple backgrounds. Extra markup means the effect will work across all browsers but if you're just trying this demo for fun, I recommend CSS3 because you can achieve much more with it.

The jQuery

Now we get to the meat of this tutorial! First thing's first, optimization! As mentioned above, my original jQuery Parallax Demo was quite a difficult script for browsers to deal with because it was asking them to do a lot. This script is much lighter thanks to the above mentioned CSS along with a good amount of jQuery optimization:

var $window = $(window);
var $firstBG = $('#intro');
var $secondBG = $('#second');
var $thirdBG = $('#third');
var $fourthBG = $('#fourth');
var trainers = $("#second .bg");

We start by turning our selectors into variables for referencing in the script later on.

We will need the height of the browser window on a few occasions so we'll put that into a variable too:

var windowHeight = $window.height();

Next up, comes our 'inview' event which uses thejQuery Element in view Event Plugin:

$('#intro, #second, #third, #fourth').bind('inview', function (event, visible) {
    if (visible == true) {
        $(this).addClass("inview");
    }else{
      $(this).removeClass("inview");
    }
});

I'm using this plugin for optimisation reasons too. Quite simply, it determines whether a particular element is 'in view' or not. I've coded it so that if one of the articles is 'in view', then it applies a class of "inview" to that article. Later on in the script, we run an "if statement" to determine whether an article is 'in view' before manipulating the position of it's background image. By doing this, it means only the background images the user can see will be moved, thus saving the script from moving all background images at the same time.

Next, we have three functions which will be called at three separate occasions. Firstly, the positioning of the unordered list I mentioned, that is fixed to the right of the window:

function RepositionNav(){
    var windowHeight = $window.height();
    var navHeight = $('#nav').height() / 2;
    var windowCenter = (windowHeight / 2);
    var newtop = windowCenter - navHeight;
    $('#nav').css({"top": newtop});
}

We want the unordered list to always remain vertically in the centre of the browser window. The code above takes the height of the list and divides it by two to find it's centre. Then it finds the centre of the window in the same way. It subtracts one value from the other to find the new top position of the unordered list and finally applies it.

Our second function is where the magic happens. It's the little bit of code that works out how to move each background image respective to the position of the scrollbar:

function newPos(x, windowHeight, pos, adjuster, inertia){ return x + "% " + (-((windowHeight + pos) - adjuster) * inertia) + "px"; }

When we call the function further in the script, we'll provide it five different arguments, which are much like settings that make each call to that function unique. They are as follows:

  • x = the horizontal position of the image
  • windowHeight = the height of the window
  • pos = The position of the scrollbar
  • adjuster = A value that moves the background image into a position we want
  • inertia = The speed at which the background image moves in relation to the scrollbar

The function then uses these arguments we've fed it to return one property that changes the CSS property, 'background-position' of whichever article we request. An example output would be:

50% -400px

That value is yet to be applied though. That's where our final function comes in.

function Move(){
    var pos = $window.scrollTop();
    ...
}

Move() is to be called whenever the user moves the scrollbar window or resizes the browser. It starts by working out the position of the scrollbar.

Then, we have several "if statements" that check to see if an article is "in view":

if($firstBG.hasClass("inview")){
    $firstBG.css({'backgroundPosition': newPos(50, windowHeight, pos, 900, 0.3)});
}

In this "if statement", we check to see if the first article has the class of "inview". Remember, our "in view" check earlier on applied this class for us. If the article is "in view", we change the CSS property, 'backgroundPosition' using the newPos function as explained above.

The function continues to test if each article has the class of "inview" and if so, it changes the "backgroundPosition" property.

You will notice in this demo, the second article has two lines of code that each change a different "backgroundPosition". One is for the full background of the second article, the other changes the background of the extra bit of markup:

<div class="bg"></div>

As I mentioned, in the fourth article I used CSS3 multiple backgrounds so when the function tests the fourth article to see if it has the class of "inview" and it does, the following code is used:

$fourthBG.css({'backgroundPosition': newPos(0, windowHeight, pos, 200, 0.9) + ", " + newPos(50, windowHeight, pos, 0, 0.7) + ", " + newPos(50, windowHeight, pos, 0, 0.5) + ", " + newPos(50, windowHeight, pos, 700, 0.3)});

The newPos function is simply called multiple times to work out the position of each background image.

One final piece of code for this function:

$('#pixels').html(pos);

At the bottom of the Nikebetterworld website, they show you how many pixels you've scrolled to reach the bottom of the page. I included this in the demo and the code above replicates that.

Whew, that's it right? Almost!

Up to now, all of that code hasn't actually done anything. We've simply set up lots of code to be activated when we need it. We just need a few events now to allow us to utilise all of those functions.

Immediately after the last function, I've called the function 'RepositionNav()'. This is triggered immediately after the script loads to make sure the unordered list of links is vertically positioned in the window.

Next up is a window resize event:

$window.resize(function(){
    Move();
    RepositionNav();
});

When a user resizes the browser, we call the Move() and RepositionNav() functions. These both make sure that the demo works well regardless of the window size.

$window.bind('scroll', function(){
    Move();
});

And finally, the scroll event. When a user scrolls, call the Move() function to move the background images relative to the position of the scrollbar.

Conclusion

Having seen the Nikebetterworld website I was blown away but a little disappointed too. I didn't get the chance to put my jQuery Parallax Demo into good use -- it just existed as a demo. Having tried to replicate the Nikebetterworld website though, I have a lot of respect for whomever it was that developed it. Not only did they create a truly awesome effect, they made the website cross browser compatible too. They also used other cutting edge technologies such as HTML5 and font replacement to make a truly amazing experience.

Parallax effects are slowly being used across more and more sites. Having made this demo and learnt a lot along the way, I still have a lot of ideas for making the demo code better but also for taking the script in new directions and achieving even more amazing effects with it. Keep your eye on my blog for more techniques, or if you beat me to it, give me a shout because I'd love to see what you've made.