I decided to spend yesterday attempting to create this slide in background effect. Through frustration and a bit of effort I managed to accomplish it! In this tutorial I’ll give you a quick run down of how to go about creating this slide in effect.
What we Want
There are a few things we want to accomplish. The code I provide below is just a quick roundup of how this effect is accomplishable. I do plan to turn this into a plugin at a later date with a much wider breadth of applicability.
- Firstly we must generate the ‘slices’ in Javascript. This will be determined by screen width and will have to change as the user resizes their window.
- Next we need the slices to slide in. We can do this with animations, but we don’t want the animations to queue in such a way that it ruins the effect
- We want the slices to reset when another animation comes on top of it.
- The slices should be positioned absolutely so content can be placed on top if it.
What we’ll use
- We’ll use jQuery and Javascript for the effect
- We’ll use this plugin to animate box shadows
- And of course, a little CSS to ensure everything looks pretty
Lets get Started
To start out we want to create a few divs in our HTML file. These will act as the containers for each of our pages. In these containers we’ll put a div called content which will contain any content you wish to add to your page. This should act as a regular div when we’re done. I’ve also included a menu but you may wish to create this differently. This code is made to work with first level divs. That means the divs with backgrounds which slide in must be direct children of the <body>
tag.
<ul id="menu"> <li><a href="" name="home">Home</a></li> <li><a href="" name="about">About</a></li> <li><a href="" name="services">Services</a></li> </ul> <div id="home"> <div class="content"> </div> </div> <div id="about"> <div class="content"> </div> </div> <div id="services"> <div class="content"> </div> </div>
Starting jQuery
Include the jQuery file as you normally would. We’re going to do a few things such as create a wrapping function for inserting functions into a queue and actually making a queue. The queue will allow the animations to run fluidly without interrupting each other. I’ve also included an object variable which contains the ID of the element we wish to give a slide in background and the direction in which it should slide in.
$(document).ready(function () { // Marks whether an animation is currently running var animationsRunning = 0; // Initial window width var initialWidth = $(window).width(); // Function wrapping code. var wrapFunction = function (fn, context, params) { return function () { fn.apply(context, params); }; }; // Global function queue var funqueue = []; // An array of the items to animate var objects = { 'home': 'vertical', 'about': 'horizontal', 'services': 'slide' }
We’re then going to create a function which will get the container from the objects variable and the direction. We will then check which direction the user wants the particular element to slide in from and use an if statement to determine how to generate objects. We’re also going to create a bunch of containers in order to keep everything ordered. More information on how everything works can be seen in the comments in the full code, found in the download link.
We use a for
statement to generate the slices depending on the screen width. I decided to use screen width since resizing can become bothersome, so making it the full screen size makes coding a little bit easier. When the slices are generated they are generated based on whether or not they have a shadow. If they have the no shadow class it means that the code
function generateSlice(mainContainer, direction) { // Get some core variables var direction = direction var mainContainer = $('#' + mainContainer); var sliceWidth = 90; // Get the height and width of the window currently var height = screen.height; var width = screen.width; // Change the width and height of the container to the height and width of the window mainContainer.css({ 'width': width + 'px', 'height': height + 'px' }); //Add a container called 'slices' to every container. if (mainContainer.find('.slices').length < 1) { mainContainer.append('<div class="slices"></div>'); } // An incremental variable var incr = 0; var theShadow = $(mainContainer).find('endShadow'); // Determine which style to apply if (direction == 'vertical') { mainContainer.addClass('vertical'); // If vertical, we need to add vertical slices. if (mainContainer.find('.vertical').length < 1) { mainContainer.children('.slices').append('<div class="vertical"></div>'); } // Divide the screen width by the slice width to get the number of slices. // Then loop this so we add a slice until x = the number of slices. for (x = 0; x < (width / sliceWidth); ++x) { mainContainer.find('.vertical').append('<div class="slice"> </div>'); } } else if (direction == 'horizontal') { // .... Continue. Full code can be found in download.
After creating the whole function it’s pretty easy to run. Just run it through an each function based on the object variable.
$.each(objects, function (k, v) { generateSlice(k, v); });
Animating
Animating is the next step. I created a function which resets the animation by checking the direction in which the elements are moving and then changing the position based on this. This function is run after the animations are finished.
Then we just have check if the animation is running, remove some classes and animate each slice. We use a random number generator to generate a random time for each slice. This means every slice slides in separately at different times.
function animation(container, direction) { if (animationsRunning > 0) { return false; } $('body > div').css({ 'z-index': '' }); $('body > div').removeClass('current-slide'); // Fade out content $('.content').fadeOut(); // The length of the animation var aLength = 500; var direction = direction; var animationType; // Set container variable $container = $('#' + container); // Change z index. $container.css({ 'z-index': '10' }); $container.addClass('current-slide'); $container.find('.slice').each(function () { // A random time so that the slices slide in at different times var randTime = Math.floor(Math.random() * (aLength + 100)) + (aLength - 100);
Then we check which direction we’re moving in and run an animation based on that. An average animation looks a little like this:
animationsRunning += 1; $(this).animate(animationType, randTime, function () { $(this).animate({ 'boxShadow': 'none' }); setTimeout(function () { $container.children('.slice').addClass('noshadow'); $('body > div:not(#' + container + ') .slice').each(function () { restartAnimation($(this)); }); $container.find('.content').fadeIn(); animationsRunning -= 1; if (animationsRunning < 1 && funqueue.length > 0) { (funqueue.shift())(); } }, (aLength)); });
After that it’s just a case of setting up a menu and checking which slide each menu item represents. We’ll also use the menu as a way to push animations into the universal queue.
$('#menu a').click(function () { if ($(this).attr('name') == 'home') { if (animationsRunning < 1) { animation('home', 'vertical'); } else { funqueue.length = 0; funqueue.push(wrapFunction(animation, this, ['home', 'vertical'])); } // ... Continue
The final (full) code with comments looks a little like this:
$(document).ready(function () { // Marks whether an animation is currently running var animationsRunning = 0; // Initial window width var initialWidth = $(window).width(); // Function wrapping code. var wrapFunction = function (fn, context, params) { return function () { fn.apply(context, params); }; }; // Global function queue var funqueue = []; // An array of the items to animate var objects = { 'home': 'vertical', 'about': 'horizontal', 'services': 'slide' } function generateSlice(mainContainer, direction) { // Get some core variables var direction = direction var mainContainer = $('#' + mainContainer); var sliceWidth = 90; // Get the height and width of the window currently var height = screen.height; var width = screen.width; // Change the width and height of the container to the height and width of the window mainContainer.css({ 'width': width + 'px', 'height': height + 'px' }); //Add a container called 'slices' to every container. if (mainContainer.find('.slices').length < 1) { mainContainer.append('<div class="slices"></div>'); } // An incremental variable var incr = 0; var theShadow = $(mainContainer).find('endShadow'); // Determine which style to apply if (direction == 'vertical') { mainContainer.addClass('vertical'); // If vertical, we need to add vertical slices. if (mainContainer.find('.vertical').length < 1) { mainContainer.children('.slices').append('<div class="vertical"></div>'); } // Divide the screen width by the slice width to get the number of slices. // Then loop this so we add a slice until x = the number of slices. for (x = 0; x < (width / sliceWidth); ++x) { mainContainer.find('.vertical').append('<div class="slice"> </div>'); } } else if (direction == 'horizontal') { mainContainer.addClass('horizontal'); // If horizontal, horizontal slices! if (mainContainer.find('.horizontal').length < 1) { mainContainer.children('.slices').append('<div class="horizontal"></div>'); } for (x = 0; x < (height / sliceWidth); ++x) { mainContainer.find('.horizontal').append('<div class="slice" style="top: ' + incr + 'px;"> </div> '); incr = incr + 90; } } else if (direction = 'slide') { mainContainer.addClass('slide'); //For sliding, we need extra containers if (mainContainer.find('.left-slices').length < 1) { mainContainer.children('.slices').append('<div class="left-slices"></div>').prepend('<div class="right-slices"></div>'); } //Then run a for statement to ensure the slices go to the correct container for (x = 0; x < (height / sliceWidth); ++x) { mainContainer.find('.left-slices').append('<div class="slice" style="top: ' + incr + 'px; left: -50%;"> </div>'); mainContainer.find('.right-slices').append('<div class="slice" style="top: ' + incr + 'px; right: -50%;"> </div>'); incr = incr + 90; } } } $.each(objects, function (k, v) { generateSlice(k, v); }); // This function completely restarts any animation that // we will run. function restartAnimation(item) { $item = item.parent('div').parent('div').parent('div'); item.css({ 'box-shadow': '' }).removeClass('noshadow'); if ($item.hasClass('horizontal')) { item.css({ 'right': '100%' }); } else if ($item.hasClass('slide')) { if (item.parent('.left-slices').length > 0) { item.css({ 'left': '-50%' }); } else { item.css({ 'right': '-50%' }); } } else if ($item.hasClass('vertical')) { item.css({ 'top': '100%' }); } } // Run the animations! function animation(container, direction) { if (animationsRunning > 0) { return false; } // Remove current slide class and z index. $('body > div').css({ 'z-index': '' }); $('body > div').removeClass('current-slide'); // Fade out content $('.content').fadeOut(); // The length of the animation var aLength = 500; var direction = direction; var animationType; // Set container variable $container = $('#' + container); // Change z index. $container.css({ 'z-index': '10' }); $container.addClass('current-slide'); $container.find('.slice').each(function () { // A random time so that the slices slide in at different times var randTime = Math.floor(Math.random() * (aLength + 100)) + (aLength - 100); // If the direction is vertical then change the animation if (direction == 'vertical') { animationType = { 'top': '0%' }; } else if (direction == 'horizontal') { animationType = { 'right': '0%' }; // Since there are two sides to the slide animation we run it separately. } else if (direction == 'slide') { if ($(this).parent('.left-slices').length > 0) { animationsRunning += 1; $(this).animate({ 'left': '0%' }, randTime, function () { $(this).animate({ 'boxShadow': 'none' }); setTimeout(function () { $('body > div:not(#' + container + ') .slice').each(function () { restartAnimation($(this)); }); $container.find('.content').fadeIn(); animationsRunning -= 1; if (animationsRunning < 1 && funqueue.length > 0) { (funqueue.shift())(); } }, (aLength+200)); }); } else if ($(this).parent('.right-slices').length > 0) { animationsRunning += 1; $(this).animate({ 'right': '0%' }, randTime, function () { $(this).animate({ 'boxShadow': 'none' }); setTimeout(function () { $('body > div:not(#' + container + ') .slice').each(function () { restartAnimation($(this)); }); $container.find('.content').fadeIn(); animationsRunning -= 1; if (animationsRunning < 1 && funqueue.length > 0) { (funqueue.shift())(); } }, (aLength+200)); }); } } if (animationType != null) { // Animation is running animationsRunning += 1; // Run animation $(this).animate(animationType, randTime, function () { // Remove box shadow at end of animation $(this).animate({ 'boxShadow': 'none' }); // Wait a little while setTimeout(function () { // For ease I'm just selecting first level divs that are not the current // animated div and restarting the animation for each of their slices. $('body > div:not(#' + container + ') .slice').each(function () { restartAnimation($(this)); }); // Fade in the content div $container.find('.content').fadeIn(); // Animation is no longer running animationsRunning -= 1; // If the animation is not running but there are still functions in the // queue then shift to the text animation if (animationsRunning < 1 && funqueue.length > 0) { (funqueue.shift())(); } }, (aLength+200)); }); } }); } // The menu $('#menu a').click(function () { // If the name attribute is 'home' if ($(this).attr('name') == 'home') { // And the animation is not running if (animationsRunning < 1) { // Then animate home vertically animation('home', 'vertical'); } else { // Otherwise the animation is running, so remove everything from the queue to // stop function buildup and add this function to the queue so it happens next funqueue.length = 0; funqueue.push(wrapFunction(animation, this, ['home', 'vertical'])); } // Otherwise check the next elements name attribute } else if ($(this).attr('name') == 'about') { if (animationsRunning < 1) { animation('about', 'horizontal'); } else { funqueue.length = 0; funqueue.push(wrapFunction(animation, this, ['about', 'horizontal'])); } } else if ($(this).attr('name') == 'services') { if (animationsRunning < 1) { animation('services', 'slide'); } else { funqueue.length = 0; funqueue.push(wrapFunction(animation, this, ['services', 'slide'])); } } // Return false so the anchor doesn't act like a regular anchor return false; }); // Run the home animation at the start since it is the first page $('#menu [name=home]').addClass('current-slide'); animation('home', 'vertical'); });
Finishing Touches
After that it’s just a case of adding some simple CSS and we’re done! You can get the full CSS (there isn’t really much to it) in the download link. Don’t forget to check out the demo!