امروز می خوایم یک منو اسلاید شو برای یک قالب رستوران بزنیم ، در نظر داشته باشید که این سبک اسلاید شو ها میتونن کاربردهای تک صفحه ایی و خاصی داشته باشن ، آیتم های منو انیمیت میشن و وقتی کلیک میشن ، باکسی برای اطلاعات بیشتر راجع به هر آیتم نمایش داده میشه . و همچنین تصویر پس زمینه با توجه به آیتم منو تغیر میکنه که حس اسلاید شو رو برای ما تداعی میکنه .

ایده براین قرار که قالب رستورانی با منویی با سبکی متفاوت بزنیم ، زمانی که روی هر منو کلیک میشه ، بقیه ی منو ها دیگه دیده نمیشن تا باکس مربوط به اطلاعات اون آیتم نمایش داده بشه .و با کلیک کردن روی آیکون بستن ، منو به حالت قبلی بر می گرده.

با کدهای HTML شروع میکنیم

ساختار کدهای HTML به این صورت خواهد بود که المانی برای تصاویر پس زمینه میسازیم که شامل المانی برای افکت روی تصویر و آیکون لودینگ است.

<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>

تصویری به عنوان پس زمینه ی اصلی انتخاب میکنیم که بعد از لود شدن قالب دیده میشه و بعد از کلیک شدن روی منو ها عکس های مربوط به هر آیتم جایگزین میشه.

محتوای منو داخل المانی با کلاس “ac_content” قرار میگیرد . عنوان رستوران را وارد میکنیم و آیتم ها منو رو داخل تگ های لیست قرار می دهیم که توسط المانی با کلاس “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 -->

داخل هر آیتم منو المانی با کلاس “ac_subitem” قرار دادیم که  محتوایی رو نمایش خواهد داد ، همچنین لینکی تعریف کردیم که قرار تصویر مربوط به آیتم رو لود کنه.

HTML به همین آسونی انجام شد. حالا CSS رو بررسی میکنیم 🙂

استایل دهی با CSS

در ابتدای استایل دهی فایل reset.css رو ایمپورت میکنیم

@import url('reset.css');

پس زمینه بدنه رو مشکی میکنیم و …

body{
	background:#000;
	color:#fff;
	font-family: 'PT Sans Narrow', Arial, sans-serif;
	text-transform:uppercase;
}

لینک ها :

a{
	color:#fff;
	text-decoration:none;
}

تصاویر پس زمینه ی آیتم های منو رو در ابتدا نمایش نخواهیم داد و از جاوا اسکریپت برای تغیر تصاویر کمک خواهیم گرفت و همچنین اندازه ی عکس ها رو با JavaScript با توجه به ابعاد مرورگر کاربر مقدار دهی میکنیم.

img.ac_bgimage{
	position:fixed;
	left:0px;
	top:0px;
	opacity:0.8;
	display:none;
}

روی عکس ها رو با افکت ریزی خواهیم پوشوند .

.ac_overlay{
	width:100%;
	height:100%;
	position:fixed;
	top:0px;
	left:0px;
	background:transparent url(../images/pattern.png) repeat top left;
}

آیکون لودینگ رو در گوشه راست-بالا ی تصویر قرار دادیم که در ابتدای لود شدن نمایش داده خواهد شد.

.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;
}

محتوای هر آیتم در وسط صفحه نمایش داده خواهد شد.

.ac_content{
	position:fixed;
	height:90px;
	width:100%;
	top:50%;
	left:0px;
	margin-top:-65px;
}

استایل های مربوط به هدر قالب.

.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;
}

استایل های مربوط به منوی اصلی ، عرض منو در ابتدا صفر خواهد بود که با استفاده از JQuery انیمت خواهد شد و به عرض مرور گر تبدیل میشود.

.ac_menu{
	background:transparent url(../images/bg_menu.png) repeat top left;
	float:left;
	position:relative;
	height:90px;
	width:0px; 
}

جهت نمایش لیست سمت راست است.

.ac_menu > ul{
	float:right;
}

ارتفاع خاصی به آیتم های لیست می دهیم و overflow رو hidden میکنیم چون لینک های داخلشون قرار با انیمت شدن نمایش داده بشن و سر جاشون قرار بگیرن که اگه این کارو نکنیم لینک ها قبل از انیمیت شدن دیده میشن.

.ac_menu > ul > li{
	float:left;
	position:relative;
	height:90px;
	overflow:hidden;
}

لینک ها با فاصله ی 60 پیکسلی از بالا و opacity صفر از دید خارج میشن ، ما اون ها رو یک به یک انیمت خواهیم کرد و بالا خواهیم برد .

.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;
}

باکس اطلاعات در عرض 400 پیکسل و مقدار ارتفاع اولیه ی صفر قرار میگیرد . همچنین فاصله ی بالای باکس رو 0 گذاشتیم تا بعد با جاوا اسکریپت باکس رو جوری انیمت کنیم که به نظر برسه از وسط صفحه به وجود میان.

.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;
}

یه مقدار استایل دهی برای باکس های اطلاعات.

.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;
}

آیکون بستن رو در گوشه ی باکس استایل دهی میکنیم.

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;
}

همین ، استایل دهی به پایان رسید ، حالا نوبت به Javascript رسید 😉

شروع به نوشتن JavaScript می کنیم.

برای ایجاد انیمت ها از کتابخونه ی JQuery Easing استفاده میکنیم که بعد از اسکریپت های JQueryلود خواهد شد.

بعضی از المان ها رو کش میکنیم و متغیر هایی رو تعریف میکنیم.

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();

تصاویر رو لود میکنیم و مسیر تصاویر رو از رو لینک ها گرفته و در آرایه ایی ذخیره کردیم.

$menuItems.each(function(i) {
	$ItemImages.push($(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
		$.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();

کار ما با جاوا اسکریپت هم تموم شد . امیدوارم لذت برده باشید 😉