Пример   Архив с исходниками

В этом уроке мы создадим асимметричный слайдер изображений с небольшим поворотом: при скольжении фотографий мы будем слегка вращать их. Необычную форму слайдера создают некоторые размещенные элементы и использованная толщина границ. Мы также добавим опцию автозапуска и функциональность прокрутки колесиком мыши.

Воспользуемся jQuery 2D Transformation Plugin для поворота изображений и jQuery Mousewheel Plugin для работы с колесиком мыши.

Разметка

Для начала обернем все элементы слайдера во враппер с классом “rm_wrapper”:

...

Внутри враппера у нас будет контейнер для слайдера, некоторые маски и угловые элементы, заголовок и скрытый слой, который будет содержать всё множество изображений:

Fashion Explosion 2012

Так в маркированном списке у нас будет первый набор из четырех изображений, где каждый элемент списка имеет несколько атрибутов данных для изображения из множества и угол поворота. Мы будем использовать эти данные чтобы знать, какие изображения придут следующими и сколько изображений должно быть повернуто.

Маски и слои с углами будут абсолютно позиционированы. Их мы разместим в верхней части слайдера и слегка повернем чтобы перекрыть некоторые области. Так как мы будем использовать тот же цвет фона для этих элементов, что и цвет фона родительского контейнера, мы создадим иллюзию формирующихся определенным образом изображений.

Добавим элементы навигации и управления автозапуском:

CSS

Во-первых, мы сбросим некоторые стили и определим свойства для body. (Если у Вас будет другой цвет фона, тонужно изменить цвет фона и границ некоторых элементов в нашем слайдере).

@import url('reset.css');
body{
	background:#f0f0f0;
	color:#000;
	font-family: 'PT Sans Narrow', Arial, sans-serif;
	font-size:16px;
}
a{
	color:#000;
	text-decoration:none;
}
h1{
	padding:10px;
	margin:20px;
	font-size:40px;
	text-transform:uppercase;
	text-shadow:0px 0px 1px #fff;
	color:#333;
	background:transparent url(../images/line.png) repeat-x bottom left;
} 

Враппер будет иметь следующий стиль:

.rm_wrapper{ width:1160px; margin:0 auto; position:relative; } 

Контейнер для слайдера будет иметь overflow: hidden

.rm_container{ width:1050px; overflow:hidden; position:relative; height:530px; margin:0 auto; }

Стиль заголовков:

 

.rm_container h2{
	background:transparent url(../images/lines.png) repeat top left;
	padding:10px 30px;
	position:absolute;
	bottom:170px;
	right:0px;
	color:#000;
	font-size:36px;
	text-transform:uppercase;
	text-shadow:1px 0px 1px #fff;
}

Нужно задать ширину списку чуть больше ширины контейнера, чтобы элементы списка находились рядом друг с другом:

.rm_container ul{ width:1170px; }

Задавая отрицательное значение левому маргину и толстую границу элемента списка, мы будем перекрывать изображения и отрезать правые части так, чтобы придать асимметричную форму вращающимся элементам. Цвет рамки будет таким же, как цвет фона body (или контейнера):

.rm_container ul li{
	float:left;
	margin-left:-80px;
	position:relative;
	overflow:hidden;
	width:310px;
	height:465px;
	border:30px solid #f0f0f0;
	border-width:50px 30px 0px 30px;
	background-color:#f0f0f0;
}

Зададим изображениям абсолютное позиционирование:

.rm_container ul li img{
	position:absolute;
	top:0px;
	left:0px;
}

Стили для маски и углов:

.rm_mask_right, .rm_mask_left{
	position: absolute;
	height: 110px;
	background: #f0f0f0;
	width: 530px;
	bottom: -30px;
	left: 0px;
	-moz-transform:rotate(-3deg);
	-webkit-transform:rotate(-3deg);
	transform:rotate(-3deg);
}
.rm_mask_right{
	left:auto;
	right:0px;
	-moz-transform:rotate(3deg);
	-webkit-transform:rotate(3deg);
	transform:rotate(3deg);
}
.rm_corner_right, .rm_corner_left{
	background: #f0f0f0;
	position:absolute;
	width:200px;
	height:100px;
	bottom:0px;
	left:-65px;
	-moz-transform:rotate(45deg);
	-webkit-transform:rotate(45deg);
	transform:rotate(45deg);
}
.rm_corner_right{
	left:auto;
	right:-65px;
	-moz-transform:rotate(-45deg);
	-webkit-transform:rotate(-45deg);
	transform:rotate(-45deg);
}

Элементы навигации будут располагаться слева и справа от основного контейнера:

.rm_nav a{
	position:absolute;
	top:200px;
	width:38px;
	height:87px;
	cursor:pointer;
	opacity:0.7;
}
.rm_nav a:hover{
	opacity:1.0;
}
.rm_nav a.rm_next{
	background:transparent url(../images/next.png) no-repeat top left;
	right:0px;
}
.rm_nav a.rm_prev{
	background:transparent url(../images/prev.png) no-repeat top left;
	left:0px;
}

Элементы управления будут помещены в левый верхний угол:

.rm_controls{
	position:absolute;
	top:0px;
	left:-40px;
	height:20px;
}
.rm_controls a{
	cursor:pointer;
	opacity:0.7;
	padding-left:24px;
	font-size:16px;
	text-transform:uppercase;
	height:20px;
	float:left;
	line-height:20px;
}
.rm_controls a:hover{
	opacity:1.0;
}
.rm_controls a.rm_play{
	display:none;
	background:transparent url(../images/play.png) no-repeat center left;
}
.rm_controls a.rm_pause{
	background:transparent url(../images/pause.png) no-repeat center left;
}

JavaScript

Основная идея функциональности слайдера — добавить другое изображение перед текущим с несколько повышенной степенью поворота, чем текущий элемент. Тогда мы будем анимированно вращать новые изображения. Начнем с кэширования некоторых элементов и проверки, если мы имеем дело с некоторыми браузерами в целях борьбы с некоторыми проблемами:

	//our 4 items
	var $listItems 		= $('#rm_container > ul > li'),
	totalItems		= $listItems.length,

	//the controls
	$rm_next		= $('#rm_next'),
	$rm_prev		= $('#rm_prev'),
	$rm_play		= $('#rm_play'),
	$rm_pause		= $('#rm_pause'),

	//the masks and corners of the slider
	$rm_mask_left	= $('#rm_mask_left'),
	$rm_mask_right	= $('#rm_mask_right'),
	$rm_corner_left	= $('#rm_corner_left'),
	$rm_corner_right= $('#rm_corner_right'),

	//check if the browser is < = IE8
	ieLte8          = ($.browser.msie && parseInt($.browser.version) <= 8),

Определим наши основные функции:

RotateImageMenu = (function() {
...
})();

RotateImageMenu.init();

И определим следующую нашу функцию:

	//difference of animation time between the items
var	timeDiff			= 300,
	//time between each image animation (slideshow)
	slideshowTime		= 3000,
	slideshowInterval,
	//checks if the images are rotating
	isRotating			= false,
	//how many images completed each slideshow iteration
	completed			= 0,
	/*
	all our images have 310 of width and 465 of height.
	this could / should be dynamically calculated
	if we would have different image sizes.

	we will set the rotation origin at
	x = width/2 and y = height*2
	*/
	origin				= ['155px', '930px'],
	init				= function() {
		configure();
		initEventsHandler();
	},
	//initialize some events
	initEventsHandler	= function() {
		/*
		next and previous arrows:
		we will stop the slideshow if active,
		and rotate each items images.
		1 	rotate right
		-1 	rotate left
		*/
		$rm_next.bind('click', function(e) {
			stopSlideshow();
			rotateImages(1);
			return false;
		});
		$rm_prev.bind('click', function(e) {
			stopSlideshow();
			rotateImages(-1);
			return false;
		});
		/*
		start and stop the slideshow
		*/
		$rm_play.bind('click', function(e) {
			startSlideshow();
			return false;
		});
		$rm_pause.bind('click', function(e) {
			stopSlideshow();
			return false;
		});
		/*
		adds events to the mouse and left / right keys
		*/
		$(document).bind('mousewheel', function(e, delta) {
			if(delta > 0) {
				stopSlideshow();
				rotateImages(0);
			}
			else {
				stopSlideshow();
				rotateImages(1);
			}
			return false;
		}).keydown(function(e){
			switch(e.which){
				case 37:
					stopSlideshow();
					rotateImages(0);
					break;
				case 39:
					stopSlideshow();
					rotateImages(1);
					break;
			}
		});
	},
	/*
	rotates each items images.
	we set a delay between each item animation
	*/
	rotateImages		= function(dir) {
		//if the animation is in progress return
		if(isRotating) return false;

		isRotating = true;

		$listItems.each(function(i) {
			var $item 				= $(this),
				/*
				the delay calculation.
				if rotation is to the right,
				then the first item to rotate is the first one,
				otherwise the last one
				*/
				interval			= (dir === 1) ? i * timeDiff : (totalItems - 1 - i) * timeDiff;

			setTimeout(function() {
					//the images associated to this item
				var	$otherImages		= $('#' + $item.data('images')).children('img'),
					totalOtherImages	= $otherImages.length;

					//the current one
					$img				= $item.children('img:last'),
					//keep track of each items current image
					current				= $item.data('current');
					//out of bounds
					if(current > totalOtherImages - 1)
						current = 0;
					else if(current < 0)
						current = totalOtherImages - 1;

					//the next image to show and its
					//initial rotation (depends on dir)
					var otherRotation	= (dir === 1) ? '-30deg' : '30deg',
						$other			= $otherImages.eq(current).clone();

					//for IE <= 8 we will not rotate,
					//but fade out / fade in ...
					//better than nothing 🙂
					if(!ieLte8)
						$other.css({
							rotate	: otherRotation,
							origin	: origin
						});

					(dir === 1) ? ++current : --current;

					//prepend the next image to the 
  • $item.data('current', current).prepend($other); //the final rotation for the current image var rotateTo = (dir === 1) ? '80deg' : '-80deg'; if(!ieLte8) { $img.animate({ rotate : rotateTo }, 1200, function(){ $(this).remove(); ++completed; if(completed === 4) { completed = 0; isRotating = false; } }); $other.animate({ rotate : '0deg' }, 600); } else { $img.fadeOut(1200, function(){ $(this).remove(); ++completed; if(completed === 4) { completed = 0; isRotating = false; } }); } }, interval ); }); }, //set initial rotations configure = function() { if($.browser.msie && !ieLte8) rotateMaskCorners(); else if(ieLte8) hideMaskCorners(); $listItems.each(function(i) { //the initial current is 1 //since we already showing the first image var $item = $(this).data('current', 1); if(!ieLte8) $item.transform({rotate: $item.data('rotation') + 'deg'}) .find('img') .transform({origin: origin}); }); }, //rotates the masks and corners rotateMaskCorners = function() { $rm_mask_left.transform({rotate: '-3deg'}); $rm_mask_right.transform({rotate: '3deg'}); $rm_corner_left.transform({rotate: '45deg'}); $rm_corner_right.transform({rotate: '-45deg'}); }, //hides the masks and corners hideMaskCorners = function() { $rm_mask_left.hide(); $rm_mask_right.hide(); $rm_corner_left.hide(); $rm_corner_right.hide(); }, startSlideshow = function() { clearInterval(slideshowInterval); rotateImages(1); slideshowInterval = setInterval(function() { rotateImages(1); }, slideshowTime); //show the pause button and hide the play button $rm_play.hide(); $rm_pause.show(); }, stopSlideshow = function() { clearInterval(slideshowInterval); //show the play button and hide the pause button $rm_pause.hide(); $rm_play.show(); }; return {init : init};
  • Пример   Архив с исходниками