From our sponsor: Meco is a distraction-free space for reading and discovering newsletters, separate from the inbox.
Today we will create a slick animated content menu with jQuery for a restaurant theme. The menu items will be animated and when clicked, a content area with more information will appear. Also, he background image is going to change according to which menu item was clicked.
The main idea is to have a restaurant menu with the different menu categories displayed. Each content are will have some foods/drinks listed. Once a content box is shown, the menu items are going to disappear. If we click on the close button to close the box, the menu items will be shown again.
So, let’s get started.
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
The Markup
The HTML structure is going to contain some elements for the background image, the grid overlay, the loading icon and the main menu. The Markup for the background is going to look like this:
<div id="ac_background" class="ac_background"> <img class="ac_bgimage" src="images/Default.jpg" alt="Background"/> <div class="ac_overlay"></div> <div class="ac_loading"></div> </div>
As a default background image when the page loads, we will have the image Default.jpg. Whenever we click on a menu item, we are going to change that image by adding another one and making the current one disappear.
The menu content will be wrapped by a div with the class “ac_content”. There will be a title and an unordered list for the items wrapped by a div with the class “ac_menu”:
<div id="ac_content" class="ac_content"> <h1><span>Cafe + Bar</span>Dhalia</h1> <div class="ac_menu"> <ul> <li> <a href="images/Appetizers.jpg">Appetizers</a> <div class="ac_subitem"> <span class="ac_close"></span> <h2>Appetizers</h2> <ul> <li> A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. </li> <li>Lobster Bisque</li> <li>Smoked Salmon Terrine</li> <li>Tuna Ceviche</li> <li>Wild Mushroom Flan</li> <li>Almond Bruschetta</li> <li>Green Chilli Canapee</li> <li>Artichoke Rucula Salad</li> </ul> </div><!-- ac_subitem--> </li> ... </ul> </div><!-- ac_menu --> </div><!-- ac_content -->
For each menu item, we will have a div “ac_subitem” which will contain the submenu content box. Inside of that box we will place another list (but it can be any other kind of content).
The link element in the main menu will have a href attribute pointing to the image that we will make appear in the background then.
Let’s take a look at the style.
The CSS
In the beginning of our CSS we will import a reset.css:
@import url('reset.css');
We will make the body black since we want to fade out the background images and make them appear darker while we do that:
body{ background:#000; color:#fff; font-family: 'PT Sans Narrow', Arial, sans-serif; text-transform:uppercase; }
The links are going to be white:
a{ color:#fff; text-decoration:none; }
The background image is going to be fixed and we don’t want to show it in the beginning – we want to take care of the appearance using JavaScript. The positioning and the size of the image is also going to be done dynamically, since we want to place it regarding to the size of the user’s window:
img.ac_bgimage{ position:fixed; left:0px; top:0px; opacity:0.8; display:none; }
The overlay is going to be placed on top of the image. It’s a repeated pattern that will give the image a neat look:
.ac_overlay{ width:100%; height:100%; position:fixed; top:0px; left:0px; background:transparent url(../images/pattern.png) repeat top left; }
We will place the loading icon in the top right corner of the page and it will appear in the beginning while we load the images:
.ac_loading{ position:fixed; top:10px; right:10px; background:#000 url(../images/loader.gif) no-repeat center center; width:50px; height:50px; border-radius:10px 10px 10px 10px; z-index:999; opacity:0.7; display:none; }
The main content div will also be fixed and placed in the middle of the screen. We set the top value to 50% and subtract half of its height from that position by setting the margin to -65 pixel. Like that we place it exactly in the middle of the webpage:
.ac_content{ position:fixed; height:90px; width:100%; top:50%; left:0px; margin-top:-65px; }
The heading to the left will have a black semi-transparent background and we will style the h1 and the span differently. We will also add a 1 pixel margin to the right in order to separate it a bit from the menu wrapper:
.ac_content h1{ background:transparent url(../images/bg_menu.png) repeat top left; display:block; float:left; width:90px; height:50px; padding:20px; font-size:36px; font-weight:bold; line-height:20px; margin-right:1px; } .ac_content h1 span{ display:block; font-weight:normal; font-size:14px; }
The main menu wrapper will have the same background like the heading and we will give it an initial width of 0 pixel. In the JavaScript we will then animate the width when we load the page to fit the width of the window:
.ac_menu{ background:transparent url(../images/bg_menu.png) repeat top left; float:left; position:relative; height:90px; width:0px; }
The unordered list which will contain the menu items will float right:
.ac_menu > ul{ float:right; }
The list items will have a specified height and the overflow will be set to hidden since we want to animate the link elements to appear from the bottom. If we wouldn’t set the overflow to hidden, we would be able to see the link elements under the menu.
.ac_menu > ul > li{ float:left; position:relative; height:90px; overflow:hidden; }
The link elements will initially be hidden by setting the margin top to 60 pixel and the opacity to 0. We will animate that margin to 0 pixel and make them opaque, in order for the items to appear, each one with a little delay, creating an interesting effect.
.ac_menu > ul > li a{ margin-top:60px; opacity:0; display:block; height:90px; padding:0px 10px; text-align:center; line-height:90px; outline:none; font-size:18px; font-weight:bold; text-shadow:1px 1px 1px #000; }
The subitem boxes will have a width of 400 pixel and an initial height of 0 pixel. We will animate that height and the top margin in order for each box to appear from the middle:
.ac_subitem{ width:400px; height:0px; /* animate to 400px */ top:50%; right:0px; margin-top:0px; /* animate to -200px */ position:fixed; z-index:99; overflow:hidden; background:transparent url(../images/bg_menu.png) repeat top left; }
Let’s give some styling to the content of the subitems:
.ac_subitem h2{ font-size:22px; font-weight:bold; color:#fff; padding: 40px 0px 0px 40px; text-shadow:0px 0px 1px #000; } .ac_subitem ul{ padding:0px 40px; } .ac_subitem ul li{ margin:10px 0px; } .ac_subitem ul li:first-child{ font-size:14px; text-transform:none; border-bottom:1px dotted #333; padding-bottom:15px; margin-bottom:15px; }
And let’s place the little closing cross at the top right corner:
span.ac_close{ float:right; margin:10px; width:11px; height:12px; cursor:pointer; background:transparent url(../images/close.png) no-repeat top left; opacity:0.4; } span.ac_close:hover{ opacity:1.0; }
And that’s all the style. Let’s add the magic!
The JavaScript
For the effects will will be using some easing, so we will need to include the jQuery Easing Plugin after including the jQuery script.
Let’s first cache some elements:
var $ac_background = $('#ac_background'), $ac_bgimage = $ac_background.find('.ac_bgimage'), $ac_loading = $ac_background.find('.ac_loading'), $ac_content = $('#ac_content'), $title = $ac_content.find('h1'), $menu = $ac_content.find('.ac_menu'), $mainNav = $menu.find('ul:first'), $menuItems = $mainNav.children('li'), totalItems = $menuItems.length, $ItemImages = new Array();
We want to preload all the image, so let’s add all the image sources that we have in the href attributes of the link elements in the menu items, and also the one of the current image:
$menuItems.each(function(i) { $ItemImages.push($(this).children('a:first').attr('href')); }); $ItemImages.push($ac_bgimage.attr('src'));
Then, let’s define our main function:
var Menu = (function(){ var init = function() { loadPage(); initWindowEvent(); }, loadPage = function() { /* 1- loads the bg image and all the item images; 2- shows the bg image; 3- shows / slides out the menu; 4- shows the menu items; 5- initializes the menu items events */ $ac_loading.show(); //show loading status image $.when(loadImages()).done(function(){ $.when(showBGImage()).done(function(){ //hide the loading status image $ac_loading.hide(); $.when(slideOutMenu()).done(function(){ $.when(toggleMenuItems('up')).done(function(){ initEventsSubMenu(); }); }); }); }); }, showBGImage = function() { return $.Deferred( function(dfd) { //adjusts the dimensions of the image to fit the screen adjustImageSize($ac_bgimage); $ac_bgimage.fadeIn(1000, dfd.resolve); } ).promise(); }, slideOutMenu = function() { /* calculate new width for the menu */ var new_w = $(window).width() - $title.outerWidth(true); return $.Deferred( function(dfd) { //slides out the menu $menu.stop() .animate({ width : new_w + 'px' }, 700, dfd.resolve); } ).promise(); }, /* shows / hides the menu items */ toggleMenuItems = function(dir) { return $.Deferred( function(dfd) { /* slides in / out the items. different animation time for each one. */ $menuItems.each(function(i) { var $el_title = $(this).children('a:first'), marginTop, opacity, easing; if(dir === 'up'){ marginTop = '0px'; opacity = 1; easing = 'easeOutBack'; } else if(dir === 'down'){ marginTop = '60px'; opacity = 0; easing = 'easeInBack'; } $el_title.stop() .animate({ marginTop : marginTop, opacity : opacity }, 200 + i * 200 , easing, function(){ if(i === totalItems - 1) dfd.resolve(); }); }); } ).promise(); }, initEventsSubMenu = function() { $menuItems.each(function(i) { var $item = $(this), // the <li> $el_title = $item.children('a:first'), el_image = $el_title.attr('href'), $sub_menu = $item.find('.ac_subitem'), $ac_close = $sub_menu.find('.ac_close'); /* user clicks on a menu item */ $el_title.bind('click.Menu', function(e) { $.when(toggleMenuItems('down')).done(function(){ openSubMenu($item, $sub_menu, el_image); }); return false; }); /* closes the submenu */ $ac_close.bind('click.Menu', function(e) { closeSubMenu($sub_menu); return false; }); }); }, openSubMenu = function($item, $sub_menu, el_image) { $sub_menu.stop() .animate({ height : '400px', marginTop : '-200px' }, 400, function() { //the bg image changes showItemImage(el_image); }); }, /* changes the background image */ showItemImage = function(source) { //if its the current one return if($ac_bgimage.attr('src') === source) return false; var $itemImage = $('<img src="'+source+'" alt="Background" class="ac_bgimage"/>'); $itemImage.insertBefore($ac_bgimage); adjustImageSize($itemImage); $ac_bgimage.fadeOut(1500, function() { $(this).remove(); $ac_bgimage = $itemImage; }); $itemImage.fadeIn(1500); }, closeSubMenu = function($sub_menu) { $sub_menu.stop() .animate({ height : '0px', marginTop : '0px' }, 400, function() { //show items toggleMenuItems('up'); }); }, /* on window resize, ajust the bg image dimentions, and recalculate the menus width */ initWindowEvent = function() { /* on window resize set the width for the menu */ $(window).bind('resize.Menu' , function(e) { adjustImageSize($ac_bgimage); /* calculate new width for the menu */ var new_w = $(window).width() - $title.outerWidth(true); $menu.css('width', new_w + 'px'); }); }, /* makes an image "fullscreen" and centered */ adjustImageSize = function($img) { var w_w = $(window).width(), w_h = $(window).height(), r_w = w_h / w_w, i_w = $img.width(), i_h = $img.height(), r_i = i_h / i_w, new_w,new_h, new_left,new_top; if(r_w > r_i){ new_h = w_h; new_w = w_h / r_i; } else{ new_h = w_w * r_i; new_w = w_w; } $img.css({ width : new_w + 'px', height : new_h + 'px', left : (w_w - new_w) / 2 + 'px', top : (w_h - new_h) / 2 + 'px' }); }, /* preloads a set of images */ loadImages = function() { return $.Deferred( function(dfd) { var total_images = $ItemImages.length, loaded = 0; for(var i = 0; i < total_images; ++i){ $('<img/>').load(function() { ++loaded; if(loaded === total_images) dfd.resolve(); }).attr('src' , $ItemImages[i]); } } ).promise(); }; return { init : init }; })(); /* call the init method of Menu */ Menu.init();
And that’s all! I hope you enjoyed the tutorial and find it useful!
Great jQuery tutorial as usual. The one thing I would add is a :hover state for the links to make it obvious they need to be clicked. I just jumped right to the demo and it wasn’t immediately obvious that I had to click the main menu items to start the action. I know, I know, the cursor turned into a pointer, but I was expecting a flyout on hover.
Thank you Brício!
@Gabe, you are absolutely right! There should be something indicating (more than just the cursor hand) that there is more on click. I would think of a little triangle next to the items… Thanks for your valuable feedback, cheers, ML
Hello from Brazil .. Mary Lou this tutorial will be very helpful for my next projects, always follow the site every day checking for news. Congratulations! 🙂
Gorgeous
Wow!! Incredible tutorial… And very nice concept.
Besides the :hover that Gabe noticed, it seems flawless. Although is it a little bit choppy on the image transitions or is that just me?
5stars regardless, blown away.
Hi Mary Lou, thats a great tutorial!
Maybe we should ease navigation : a mouseclick outside the box closes id and switches back to the menu.
What do you think about it?
thanks for u mary
You always doing top notch tutorials, keep doing it, best regards.
Absolutely Awesome Stuffs!!! Keep it up!!! Your Site Rocks!!!
Great tut. Please make at the next time please make more tutorials again 😀
Pretty amazing. I wish you can even do something like move the nav to top and show content in the centered horz and vert. That would be another idea but still pretty awesome!
Awesome tut as always, Mary. Your tuts fill the missing link b/w Flash and jQuery 😀
Thanks for this great menu tutorial.You give me a lot of concept to do more with this menu!
That’s great !
this is fantastic. Very very beautiful, and great explanation. Thank you very much!
awesome.. i think.. there will be a time when flash will only used for complex animations..
nice, but it took me a while to find the way ( Button : X ) to go back to the Home page.
it was in far up/right corner, kind of hidden.
maybe it’s better to simply change it to:
Back !
otherwise i love the concept.
😛
thank again
Thanks for the tutorial Mary. I’m a newbie and have a question. How would one add links to other pages within the menu?
thanks a lot for this great tutorial Mary!!!
thanks a lot for this wonderful tutorial!! 🙂 ..just a stupid question, how can I add a logo (image) instead of the name of the place?? I have problems with the code…
Wow, this is just like a complete flash website… nice looks..
thanks..
Just insane… flash is not required. Great Tuto… thanks.
hi
very good tutorial its very help full for me
thanks thaks
Am I mising something, why do absolutely none of your tuts work with ie9?
Respectfully…
awesome!
but how would you add links, inside the menu?
It’s awesome! Thanks!
Hi;
I did something like this in flash a couple of mounths ago, if you want to see it – http://burnandbass.com/maggi/ . It is triggered by hover, not on click but the idei is the same. Great tutorial as usual!
Very beautiful and good effect.
Hi! someone know how to add a link inside the ac_subitem div (inside menu content)????
Think i got it!!!!
.ac_menu > ul > li .ac_subitem a{
opacity:100
}
rigth?
Congrats from Brazil… nice tuto!!.. very helpful …. cya!
That actually made me say ‘Sweet!’…very awesome tutorial.
Hi, I think your tut is amazing, really, but I want to do more….for instance, when you go to Appetizers, why don’t click on the first line (lobster I believe) and try to open a “lobster page” with a background picture and some hints related to the recipe on the “black menu”?
@ oscar: could you please explain me exactly how you can?
nice work and nice tutorial!!!
The effect of the menu is simply amazing. In the next tutorial you could explain more detailed the pieces of javascript code.
That really amazing I love’d it
thankyou so much
Mary Lou,
One more time Great tutorial !
Thanks for sharing your talent and inspiration with us !
Hi Mary Lou
As you can see I used your template for a website. I still got some questions, since I’m not really a webdesigner.
I made this website ( 213.148.252.79 ) for a voluntary youthorganisation, where I work voluntary as well.
The only problem I encountered during the sitemaking is that I can’t use href ‘s in the text. They result in one clickable nothing.
So is there some workaround?
greets Toine
Hi Toine,
it looks awesome! Your link elements don’t appear because of the style that I’ve set for “.ac_menu > ul > li a”. Just go to line 84 in the style.css and change
.ac_menu > ul > li a
to
.ac_menu > ul > li > a
You see, like that we will not address all the “a” elements and your links inside of the content boxes will look “normal”
Hope it helps,
cheers,
ML
Thank you very much for that information and the beautiful tutorial.
Hi,
This is realy great!.
Hi Mary Lou,
Is it possible to add a lightbox link to one off the links like mentioned byToine? I have tried to juse a colorbox.js but it does not work (I am complete new to this)
regards
Michel
Mary Lou,
I’m trying to implement your awesome scripts/page into a WordPress installation at http://whatsthe.webfactional.com/. I’ve racked my brain with two javascript errors:
jQuery is not defined
http://whatsthe.webfactional.com/js/jquery.easing.1.3.js?ver=3.1
Line 39
and
$ is not a function
http://whatsthe.webfactional.com/
Line 444
I assume is a conflict with other scripts loading though I have yet been unable to fix. Any advice would be greatly appreciated.
Thanks
Hi David,
you need to include the jquery script before including the easing one (btw you are including the easing twice).
Hope it helps, cheers, ML
Mary Lou, Thanks for your reply! I removed the first instance of easing, and moved the jquery before the easing. It’s still coughing up an error: $ is not a function
http://whatsthe.webfactional.com/
Line 432.
Hi David,
I guess the problem is this line:
var $j = jQuery.noConflict();
you have this in the tabs.js file. If you do it like that, then you need to use $j when referencing jQuery (instead of $).
Cheers, ML
Are you available for a fix?
Here is mine and it’s still complaining this time when it reaches starts loading status image;
$j(function() {
var $ac_background = $j(‘#ac_background’),
$ac_bgimage = $ac_background.find(‘.ac_bgimage’),
$ac_loading = $ac_background.find(‘.ac_loading’),
$ac_content = $j(‘#ac_content’),
$title = $ac_content.find(‘h1’),
$menu = $ac_content.find(‘.ac_menu’),
$mainNav = $menu.find(‘ul:first’),
$menuItems = $mainNav.children(‘li’),
totalItems = $menuItems.length,
$ItemImages = new Array();
David, I guess there are still $’s that need to be replaced with $j’s 🙂
Cheers, ML
Do you have time to fix it for me?
I think I’ve fixed it. Here was my script for anyone that may have the same issue:
var $j = jQuery.noConflict();
$j(function() {
var $ac_background = $j(‘#ac_background’),
$ac_bgimage = $ac_background.find(‘.ac_bgimage’),
$ac_loading = $ac_background.find(‘.ac_loading’),
$ac_content = $j(‘#ac_content’),
$title = $ac_content.find(‘h1’),
$menu = $ac_content.find(‘.ac_menu’),
$mainNav = $menu.find(‘ul:first’),
$menuItems = $mainNav.children(‘li’),
totalItems = $menuItems.length,
$ItemImages = new Array();
/*
for this menu, we will preload all the images.
let’s add all the image sources to an array,
including the bg image
*/
$menuItems.each(function(i) {
$ItemImages.push($j(this).children(‘a:first’).attr(‘href’));
});
$ItemImages.push($ac_bgimage.attr(‘src’));
var Menu = (function(){
var init = function() {
loadPage();
initWindowEvent();
},
loadPage = function() {
/*
1- loads the bg image and all the item images;
2- shows the bg image;
3- shows / slides out the menu;
4- shows the menu items;
5- initializes the menu items events
*/
$ac_loading.show();//show loading status image
$j.when(loadImages()).done(function(){
$j.when(showBGImage()).done(function(){
//hide the loading status image
$ac_loading.hide();
$j.when(slideOutMenu()).done(function(){
$j.when(toggleMenuItems(‘up’)).done(function(){
initEventsSubMenu();
});
});
});
});
},
showBGImage = function() {
return $j.Deferred(
function(dfd) {
//adjusts the dimensions of the image to fit the screen
adjustImageSize($ac_bgimage);
$ac_bgimage.fadeIn(1000, dfd.resolve);
}
).promise();
},
slideOutMenu = function() {
/* calculate new width for the menu */
var new_w = $j(window).width() – $title.outerWidth(true);
return $j.Deferred(
function(dfd) {
//slides out the menu
$menu.stop()
.animate({
width : new_w + ‘px’
}, 700, dfd.resolve);
}
).promise();
},
/* shows / hides the menu items */
toggleMenuItems = function(dir) {
return $j.Deferred(
function(dfd) {
/*
slides in / out the items.
different animation time for each one.
*/
$menuItems.each(function(i) {
var $el_title = $j(this).children(‘a:first’),
marginTop, opacity, easing;
if(dir === ‘up’){
marginTop = ‘0px’;
opacity = 1;
easing = ‘easeOutBack’;
}
else if(dir === ‘down’){
marginTop = ’60px’;
opacity = 0;
easing = ‘easeInBack’;
}
$el_title.stop()
.animate({
marginTop : marginTop,
opacity : opacity
}, 200 + i * 200 , easing, function(){
if(i === totalItems – 1)
dfd.resolve();
});
});
}
).promise();
},
initEventsSubMenu = function() {
$menuItems.each(function(i) {
var $item = $j(this), // the
$el_title = $item.children(‘a:first’),
el_image = $el_title.attr(‘href’),
$sub_menu = $item.find(‘.ac_subitem’),
$ac_close = $sub_menu.find(‘.ac_close’);
/* user clicks one item : appetizers | main course | desserts | wines | specials */
$el_title.bind(‘click.Menu’, function(e) {
$j.when(toggleMenuItems(‘down’)).done(function(){
openSubMenu($item, $sub_menu, el_image);
});
return false;
});
/* closes the submenu */
$ac_close.bind(‘click.Menu’, function(e) {
closeSubMenu($sub_menu);
return false;
});
});
},
openSubMenu = function($item, $sub_menu, el_image) {
$sub_menu.stop()
.animate({
height : ‘400px’,
marginTop : ‘-200px’
}, 400, function() {
//the bg image changes
showItemImage(el_image);
});
},
/* changes the background image */
showItemImage = function(source) {
//if its the current one return
if($ac_bgimage.attr(‘src’) === source)
return false;
var $itemImage = $j(”);
$itemImage.insertBefore($ac_bgimage);
adjustImageSize($itemImage);
$ac_bgimage.fadeOut(1500, function() {
$j(this).remove();
$ac_bgimage = $itemImage;
});
$itemImage.fadeIn(1500);
},
closeSubMenu = function($sub_menu) {
$sub_menu.stop()
.animate({
height : ‘0px’,
marginTop : ‘0px’
}, 400, function() {
//show items
toggleMenuItems(‘up’);
});
},
/*
on window resize, ajust the bg image dimentions,
and recalculate the menus width
*/
initWindowEvent = function() {
/* on window resize set the width for the menu */
$j(window).bind(‘resize.Menu’ , function(e) {
adjustImageSize($ac_bgimage);
/* calculate new width for the menu */
var new_w = $j(window).width() – $title.outerWidth(true);
$menu.css(‘width’, new_w + ‘px’);
});
},
/* makes an image “fullscreen” and centered */
adjustImageSize = function($img) {
var w_w = $j(window).width(),
w_h = $j(window).height(),
r_w = w_h / w_w,
i_w = $img.width(),
i_h = $img.height(),
r_i = i_h / i_w,
new_w,new_h,
new_left,new_top;
if(r_w > r_i){
new_h = w_h;
new_w = w_h / r_i;
}
else{
new_h = w_w * r_i;
new_w = w_w;
}
$img.css({
width : new_w + ‘px’,
height : new_h + ‘px’,
left : (w_w – new_w) / 2 + ‘px’,
top : (w_h – new_h) / 2 + ‘px’
});
},
/* preloads a set of images */
loadImages = function() {
return $j.Deferred(
function(dfd) {
var total_images = $ItemImages.length,
loaded = 0;
for(var i = 0; i < total_images; ++i){
$j('’).load(function() {
++loaded;
if(loaded === total_images)
dfd.resolve();
}).attr(‘src’ , $ItemImages[i]);
}
}
).promise();
};
return {
init : init
};
})();
/*
call the init method of Menu
*/
Menu.init();
});
Nope… I messed up the background image aspect ratio 🙁
Sorry for the long posts though I’d like to share my FINAL fix:
var $j = jQuery.noConflict();
$j(function() {
var $ac_background = $j(‘#ac_background’),
$ac_bgimage = $ac_background.find(‘.ac_bgimage’),
$ac_loading = $ac_background.find(‘.ac_loading’),
$ac_content = $j(‘#ac_content’),
$title = $ac_content.find(‘h1’),
$menu = $ac_content.find(‘.ac_menu’),
$mainNav = $menu.find(‘ul:first’),
$menuItems = $mainNav.children(‘li’),
totalItems = $menuItems.length,
$ItemImages = new Array();
/*
for this menu, we will preload all the images.
let’s add all the image sources to an array,
including the bg image
*/
$menuItems.each(function(i) {
$ItemImages.push($j(this).children(‘a:first’).attr(‘href’));
});
$ItemImages.push($ac_bgimage.attr(‘src’));
var Menu = (function(){
var init = function() {
loadPage();
initWindowEvent();
},
loadPage = function() {
/*
1- loads the bg image and all the item images;
2- shows the bg image;
3- shows / slides out the menu;
4- shows the menu items;
5- initializes the menu items events
*/
$ac_loading.show();//show loading status image
$j.when(loadImages()).done(function(){
$j.when(showBGImage()).done(function(){
//hide the loading status image
$ac_loading.hide();
$j.when(slideOutMenu()).done(function(){
$j.when(toggleMenuItems(‘up’)).done(function(){
initEventsSubMenu();
});
});
});
});
},
showBGImage = function() {
return $j.Deferred(
function(dfd) {
//adjusts the dimensions of the image to fit the screen
adjustImageSize($ac_bgimage);
$ac_bgimage.fadeIn(1000, dfd.resolve);
}
).promise();
},
slideOutMenu = function() {
/* calculate new width for the menu */
var new_w = $j(window).width() – $title.outerWidth(true);
return $j.Deferred(
function(dfd) {
//slides out the menu
$menu.stop()
.animate({
width : new_w + ‘px’
}, 700, dfd.resolve);
}
).promise();
},
/* shows / hides the menu items */
toggleMenuItems = function(dir) {
return $j.Deferred(
function(dfd) {
/*
slides in / out the items.
different animation time for each one.
*/
$menuItems.each(function(i) {
var $el_title = $j(this).children(‘a:first’),
marginTop, opacity, easing;
if(dir === ‘up’){
marginTop = ‘0px’;
opacity = 1;
easing = ‘easeOutBack’;
}
else if(dir === ‘down’){
marginTop = ’60px’;
opacity = 0;
easing = ‘easeInBack’;
}
$el_title.stop()
.animate({
marginTop : marginTop,
opacity : opacity
}, 200 + i * 200 , easing, function(){
if(i === totalItems – 1)
dfd.resolve();
});
});
}
).promise();
},
initEventsSubMenu = function() {
$menuItems.each(function(i) {
var $item = $j(this), // the
$el_title = $item.children(‘a:first’),
el_image = $el_title.attr(‘href’),
$sub_menu = $item.find(‘.ac_subitem’),
$ac_close = $sub_menu.find(‘.ac_close’);
/* user clicks one item : appetizers | main course | desserts | wines | specials */
$el_title.bind(‘click.Menu’, function(e) {
$j.when(toggleMenuItems(‘down’)).done(function(){
openSubMenu($item, $sub_menu, el_image);
});
return false;
});
/* closes the submenu */
$ac_close.bind(‘click.Menu’, function(e) {
closeSubMenu($sub_menu);
return false;
});
});
},
openSubMenu = function($item, $sub_menu, el_image) {
$sub_menu.stop()
.animate({
height : ‘400px’,
marginTop : ‘-200px’
}, 400, function() {
//the bg image changes
showItemImage(el_image);
});
},
/* changes the background image */
showItemImage = function(source) {
//if its the current one return
if($ac_bgimage.attr(‘src’) === source)
return false;
var $itemImage = $j(”);
$itemImage.insertBefore($ac_bgimage);
adjustImageSize($itemImage);
$ac_bgimage.fadeOut(1500, function() {
$j(this).remove();
$ac_bgimage = $itemImage;
});
$itemImage.fadeIn(1500);
},
closeSubMenu = function($sub_menu) {
$sub_menu.stop()
.animate({
height : ‘0px’,
marginTop : ‘0px’
}, 400, function() {
//show items
toggleMenuItems(‘up’);
});
},
/*
on window resize, ajust the bg image dimentions,
and recalculate the menus width
*/
initWindowEvent = function() {
/* on window resize set the width for the menu */
$j(window).bind(‘resize.Menu’ , function(e) {
adjustImageSize($ac_bgimage);
/* calculate new width for the menu */
var new_w = $j(window).width() – $title.outerWidth(true);
$menu.css(‘width’, new_w + ‘px’);
});
},
/* makes an image “fullscreen” and centered */
adjustImageSize = function($img) {
var w_w = $j(window).width(),
w_h = $j(window).height(),
r_w = w_h / w_w,
i_w = $img.width(),
i_h = $img.height(),
r_i = i_h / i_w,
new_w,new_h,
new_left,new_top;
if(r_w > r_i){
new_h = w_h;
new_w = w_h / r_i;
}
else{
new_h = w_w * r_i;
new_w = w_w;
}
$img.css({
width : new_w + ‘px’,
height : new_h + ‘px’,
left : (w_w – new_w) / 2 + ‘px’,
top : (w_h – new_h) / 2 + ‘px’
});
},
/* preloads a set of images */
loadImages = function() {
return $j.Deferred(
function(dfd) {
var total_images = $ItemImages.length,
loaded = 0;
for(var i = 0; i < total_images; ++i){
$j('’).load(function() {
++loaded;
if(loaded === total_images)
dfd.resolve();
}).attr(‘src’ , $ItemImages[i]);
}
}
).promise();
};
return {
init : init
};
})();
/*
call the init method of Menu
*/
Menu.init();
});
Amazing tutorial! I’ve redone my website with this one!
Looks awesome Wyatt! Love the space theme 🙂 Cheers, ML