Галерея в стиле Polaroid

В данном уроке мы создадим галерею в стиле фотографий Polaroid. Альбомы будут представлены набором немного повернутых миниатюр, которые будут "всплывать" при наведении курсора мыши. Полноразмерные изображения будут подниматься снизу, если кнопка мыши будет нажата на соответствующей миниатюре. В режиме просмотра полноразмерного изображения пользователь может перемещаться между изображениями или просто выбирать другое, нажав на соответствующей миниатюре.

Для данной галереи используется плагин 2D Transform для анимации вращений.

Прекрасные фотографии сделаны талантливым автором Tetsumo. Вы можете посетить его блог.

Начнем!

demosourse

Разметка

Мы собираемся включить весь наш HTML в элемент div с классом “pp_gallery”. Код будет состоять из элементов div индикатора загрузки и навигации, которая появляется  только при просмотре полноразмерного изображения, а также основного контейнера для миниатюр.

Внутри контейнера для миниатюр, который имеет класс “pp_thumbContainer”, мы размещаем несколько элементов div для альбомов и элемент div для возвращения к виду выбора альбомов. Каждый альбом содержит миниатюры  с описаниями, помещенные в div с классом “content”. Также имеется элемент div для описания альбома.

<div id="pp_gallery" class="pp_gallery">
<div id="pp_loading" class="pp_loading"></div>
<div id="pp_next" class="pp_next"></div>
<div id="pp_prev" class="pp_prev"></div>
<div id="pp_thumbContainer">
<div class="album">
<div class="content">
<img src="images/album1/thumbs/1.jpg" alt="images/album1/1.jpg" />
<span>Шестидесятые. Автор: Tetsumo</span>
</div>
					
			. . .
<div class="descr">
				Шестидесятые
</div>
</div>
<div class="album">
<div class="content">
<img src="images/album2/thumbs/1.jpg" alt="images/album2/1.jpg" />
<span>Девочка-бабочка</span>
</div>
					
			. . .  
<div class="descr">
				Портреты
</div>
</div>
<div id="pp_back" class="pp_back">Альбомы</div>
</div>
</div>

Структура  HTML кода для динамически создаваемого полноразмерного изображения будет следующая:

<div id="pp_preview" class="pp_preview">
<img src="images/album1/1.jpg" />
<div class="pp_descr"><span>Описание</span></div>
</div>

CSS

Начинаем со сброса и задания общих стилей для тега body:

*{
	margin:0;
	padding:0;
}
body{
	background:#000 url(../bg.jpg) repeat center center;
	font-family:"Myriad Pro", "Trebuchet MS", Helvetica, sans-serif;
	font-size:12px;
	color: #fff;
	overflow:hidden;
}

Структурированные фоны заполняют пространство окна очень хорошо. Поэтому мы используем текстуру дерева. В этой заметке блога Webtreats можно найти набор различных природных текстур.

Затем, мы задаем стиль для элементов div индикатора загрузки и навигации в режиме предварительного просмотра:

.pp_loading{
	display:none;
	position:fixed;
	top:50%;
	left:50%;
	margin:-35px 0px 0px -35px;
	background:#fff url(../icons/loader.gif) no-repeat center center;
	width:70px;
	height:70px;
	z-index:999;
	opacity:0.7;
	-moz-border-radius:10px;
	-webkit-border-radius:10px;
	border-radius:10px;
}
.pp_next, .pp_prev{
	cursor:pointer;
	top:50%;
	margin-top:-16px;
	width:32px;
	height:32px;
	position:fixed;
	text-align:center;
	border:1px solid #111;
	color:#fff;
	-moz-box-shadow:0px 0px 3px #000;
	-webkit-box-shadow:0px 0px 3px #000;
	box-shadow:0px 0px 3px #000;
}
.pp_next{
	right:-40px;
	background:#222 url(../icons/next.png) no-repeat center center;
}
.pp_prev{
	left:-40px;
	background:#222 url(../icons/prev.png) no-repeat center center;
}

Индикатор загрузки центрируется на странице с помощью трюка  "50% отрицательного значения поля". Когда позиция фиксированная, мы можем установить свойствам top и left 50% и добавить сверху и слева поле с отрицательным значением половины ширины элемента, или высоты, соответственно.

Тоже самое действие делаем и для элементов навигации, только центрируем их по вертикали.

Контейнер миниатюр позиционируется снизу страницы:

#pp_thumbContainer{
	position:fixed;
	bottom:0px;
	left:0px;
	height:65px;
	width:100%;
}

Альбомы изначально скрыты. При загрузке страницы они поднимаются снизу, поэтому устанавливаем для них начальную позицию -90 пикселей:

#pp_thumbContainer .album{
	position:absolute;
	width:200px;
	height:65px;
	bottom:-90px;
}

Значение свойства left для позиционирования альбома будет динамически вычисляться при загрузке страницы. Альбомы будут равномерно распределяться по ширине страницы.

Описание альбома и фоновый элемент будут иметь общие стили:

.album .descr,
.pp_back{
	position:absolute;
	bottom:0px;
	left:-16px;
	background:#222;
	text-align:center;
	border:1px solid #111;
	padding:5px;
	cursor:pointer;
	width:169px;
	color:#fff;
	cursor:pointer;
	text-shadow:0px 0px 1px #fff;
	-moz-box-shadow:1px 1px 4px #000;
	-webkit-box-shadow:1px 1px 4px #000;
	box-shadow:1px 1px 4px #000;
}

…но не все. Мы перепишем и добавим значения, которые специфичны для класса .pp_back:

.pp_back{
	text-transform:uppercase;
	bottom:120px;
	left:-100px;
	width:80px;
}

Элемент для изображения и его описания будет иметь следующие стили:

#pp_thumbContainer .content{
	position:absolute;
	top:0px;
	height:155px;
	cursor:pointer;
}

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

Миниатюры будут иметь белую окантовку и тень:

#pp_thumbContainer img{
	border:5px solid #fff;
	-moz-box-shadow:1px 1px 7px #000;
	-webkit-box-shadow:1px 1px 7px #000;
	box-shadow:1px 1px 7px #000;
}

Описание каждого изображения невидимое. Мы будем использовать его для заполнения элемента описания в режиме просмотра полноразмерного изображения.

#pp_thumbContainer .content span{
	display:none;
}

Элемент  для полноразмерного изображения позиционируется за пределами страницы с помощью установки значения top в 150%. Как только изображение загружено, мы поднимаем его снизу. Мы устанавливаем значение left в 50% так как хотим центрировать картинку. Так как мы еще не знаем ширину и высоту изображения, мы не можем установить отрицательное значение поля. Это будет делаться динамически в коде JavaScript.

.pp_preview{
	position:fixed;
	top:150%;
	left:50%;
}

Полноразмерное изображение будет иметь большую белую рамку снизу, куда будет вставляться описание:

.pp_preview img{
	position:absolute;
	top:0px;
	left:0px;
	border:10px solid #fff;
	border-bottom:45px solid #fff;
	-moz-box-shadow:1px 1px 7px #000;
	-webkit-box-shadow:1px 1px 7px #000;
	box-shadow:1px 1px 7px #000;
}

Описание будет располагаться внизу элемента изображения. В коде JavaScript мы динамически установим ширину и высоту элемента div для изображения, поэтому описание будет позиционироваться на толстой нижней рамке изображения.

.pp_descr{
	height:45px;
	line-height:45px;
	font-size:20px;
	width:100%;
	bottom:0px;
	left:0px;
	position:absolute;
	text-align:center;
	color:#00021c;
}

Также мы задействуем Cufon для использования рукописного шрифта в описании.

JavaScript

В этом проекте будет много анимации, выполненной с помощью JavaScript. Альбомы будут выдвигаться снизу во время загрузки страницы. Как только на альбоме нажать кнопку мыши, миниатюры будут распределяться равномерно внизу страницы. При выборе миниатюры будет создаваться элемент для полноразмерного изображения и выдвигаться снизу. Размер изображения будет изменяться, чтобы соответствовать размеру окна.

Начнем с определения переменных. Сначала нужно определиться, имеем ли мы дело с Internet Explorer, так как для него не нужно будет использовать вращение полноразмерного изображения:

var ie 			= false;
if ($.browser.msie) {
	ie = true;
}

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

//Будет выводиться текущий альбом/изображение?
var current		= -1;
var album		= -1;
//Ширина окна
var w_width 	        = $(window).width();
//Кэширование
var $albums 	        = $('#pp_thumbContainer div.album');
var $loader		= $('#pp_loading');
var $next		= $('#pp_next');
var $prev		= $('#pp_prev');
var $images		= $('#pp_thumbContainer div.content img');
var $back		= $('#pp_back');

Мы хотим распределять альбомы равномерно по ширине страницы, поэтому надо вычислить соответствующее значение для свойства left:

//Мы хотим распределять альбомы равномерно 
//с промежутками равными ширине/(количество альбомов + 1)
var nmb_albums	= $albums.length;
var spaces 		= w_width/(nmb_albums+1);
var cnt			= 0;
//Предварительная загрузка изображений (миниатюр)
var nmb_images	= $images.length;
var loaded  	= 0;
$images.each(function(i){
	var $image = $(this);
	$('<img />').load(function(){
		++loaded;
		if(loaded == nmb_images){
			//Распределяем альбомы равномерно внизу страницы
			$albums.each(function(){
				var $this 	= $(this);
				++cnt;
				var left	= spaces*cnt - $this.width()/2;
				$this.css('left',left+'px');
				$this.stop().animate({'bottom':'0px'},500);
			}).unbind('click').bind('click',spreadPictures);
			//Также поворачиваем каждое изображение в альбоме на произвольный угол
			$images.each(function(){
				var $this 	= $(this);
				var r 		= Math.floor(Math.random()*41)-20;
				$this.transform({'rotate'	: r + 'deg'});
			});
		}
	}).attr('src', $image.attr('src'));
});

Функция spreadPictures выполняет похожую операцию: она смещает выбранный альбом влево и распределяет все миниатюры равномерно:

function spreadPictures(){
	var $album 	= $(this);
	//Отслеживаем, какой альбом открыт
	album		= $album.index();
	//Скрываем все другие альбомы
	$albums.not($album).stop().animate({'bottom':'-90px'},300);
	$album.unbind('click');
	//Смещаем текущий альбом влево
	//и одновременно распределяем его изображения,
	//поворачивая их случайным образом. Также скрываем описание альбома
			
	//Сохраняем текущее левое для обратной операции
	$album.data('left',$album.css('left'))
		  .stop()
		  .animate({'left':'0px'},500).find('.descr').stop().animate({'bottom':'-30px'},200);
	var total_pic 	= $album.find('.content').length;
	var cnt			= 0;
	//Каждое изображение
	$album.find('.content')
		  .each(function(){
		var $content = $(this);
		++cnt;
		//Ширина окна
		var w_width 	= $(window).width();
		var spaces 		= w_width/(total_pic+1);
		var left		= (spaces*cnt) - (140/2);
		var r 			= Math.floor(Math.random()*41)-20;
		//var r = Math.floor(Math.random()*81)-40;
		$content.stop().animate({'left':left+'px'},500,function(){
			$(this).unbind('click')
					.bind('click',showImage)
				   .unbind('mouseenter')
				   .bind('mouseenter',upImage)
				   .unbind('mouseleave')
				   .bind('mouseleave',downImage);
		}).find('img')
		  .stop()
		  .animate({'rotate': r+'deg'},300);
		$back.stop().animate({'left':'0px'},300);
	});
}

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

//Переход обратно к виду альбомов.
//Текущий альбом получает свою исходную позицию,
//все остальные альбомы поднимаются вверх,
//текущее изображение убирается с экрана
$back.bind('click',function(){
	$back.stop().animate({'left':'-100px'},300);
	hideNavigation();
	if(current != -1){
		hideCurrentPicture();
	}
					
	var $current_album = $('#pp_thumbContainer div.album:nth-child('+parseInt(album+1)+')');
	$current_album.stop()
				  .animate({'left':$current_album.data('left')},500)
				  .find('.descr')
				  .stop()
				  .animate({'bottom':'0px'},500);
				
	$current_album.unbind('click')
				  .bind('click',spreadPictures);
					
	$current_album.find('.content')
			    .each(function(){
					var $content = $(this);
					$content.unbind('mouseenter mouseleave click');
					$content.stop().animate({'left':'0px'},500);
				});
								
	$albums.not($current_album).stop().animate({'bottom':'0px'},500);
});

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

//Выводим изображение (соответствующее выбранной миниатюре) в центре страницы
function showImage(nav){
	if(!enableshow) return;
	enableshow = false;
	if(nav == 1){
		//Дошли до первого изображения
		if(current==0){
			enableshow = true;
			return;
		}
		var $content = $('#pp_thumbContainer div.album:nth-child('+parseInt(album+1)+')')
							  .find('.content:nth-child('+parseInt(current)+')');
		//Дошли до последнего изображения
		if($content.length==0){
			enableshow = true;
			current-=2;
			return;
		}	
	}
	else
		var $content 			= $(this);
			
	//Показываем индикатор загрузки AJAX
	$loader.show();
				
	if(current != -1){
		hideCurrentPicture();
	}
					
	current 				= $content.index();
	var $thumb				= $content.find('img');
	var imgL_source 	 	= $thumb.attr('alt');
	var imgL_description 	= $thumb.next().html();
	//Предварительно загружаем большое изображение
	$('<img style=""/>').load(function(){
		var $imgL 	= $(this);
		//Изменяем размер изображения на основе размера окна
		resize($imgL);
		//Создаем элемент для включения большого изображения
		//и его описания
		var $preview = $('<div />',{
			'id'		: 'pp_preview',
			'className'	: 'pp_preview',
			'html'     	: '<div class="pp_descr"><span>'+imgL_description+'</span></div>',
			'style'		: 'visibility:hidden;'
		});
		$preview.prepend($imgL);
		$('#pp_gallery').prepend($preview);
						
		var largeW 				= $imgL.width()+20;
		var largeH 				= $imgL.height()+10+45;
		//Изменяем свойства контейнера div, 
		//чтобы он соответствовал большому изображению
		$preview.css({
			'width'			:largeW+'px',
			'height'		:largeH+'px',
			'marginTop'		:-largeH/2-20+'px',
			'marginLeft'	:-largeW/2+'px',
			'visibility'	:'visible'
		});
		Cufon.replace('.pp_descr');
		//Показываем элементы навигации
		showNavigation();
						
		//Скрываем индикатор загрузки AJAX
		$loader.hide();
						
		//Поднимаем (одновременно поворачивая) большое изображение
		var r 			= Math.floor(Math.random()*41)-20;
		if(ie)
			var param = {
				'top':'50%'
			};
		else
			var param = {
				'top':'50%',
				'rotate': r+'deg'
			};
		$preview.stop().animate(param,500,function(){
			enableshow = true;
		});
	}).error(function(){
	//Ошибка загрузки изображения. Может быть, вывести сообщение: 'Недоступен предварительный просмотр'?
	}).attr('src',imgL_source);	
}

Следующие две функции обрабатывают навигацию по изображениям:

//Кнопка мыши нажата на кнопке "следующее"
$next.bind('click',function(){
	current+=2;
	showImage(1);
});
				
//кнопка мыши нажата на кнопке "предыдущее"
$prev.bind('click',function(){
	showImage(1);
});

Функция hideCurrentPicture сдвигает полноразмерное изображение за пределы окна. В браузере IE не будет использоваться никаких анимаций вращения:

//Поднимаем текущее изображение
function hideCurrentPicture(){
	current = -1;
	var r 	= Math.floor(Math.random()*41)-20;
	if(ie)
		var param = {
			'top':'-150%'
		};
	else
		var param = {
			'top':'-150%',
			'rotate': r+'deg'
		};
	$('#pp_preview').stop()
					.animate(param,500,function(){
						$(this).remove();
					});
}

Функции showNavigation и hideNavigation обеспечивают вывод и скрытие элементов навигации:

//Показываем кнопки навигации
function showNavigation(){
	$next.stop().animate({'right':'0px'},100);
	$prev.stop().animate({'left':'0px'},100);
}
				
//Скрываем кнопки навигации
function hideNavigation(){
	$next.stop().animate({'right':'-40px'},300);
	$prev.stop().animate({'left':'-40px'},300);
}

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

//Событие mouseenter для каждой миниатюры
function upImage(){
	var $content 	= $(this);
	$content.stop().animate({
		'marginTop'		: '-70px'
	},400).find('img')
		  .stop()
		  .animate({'rotate': '0deg'},400);
}

Когда курсор мыши уходит с миниатюры, она опускается обратно и поворачивается на случайный угол:

//Событие mouseleave для каждой миниатюры
function downImage(){
	var $content 	= $(this);
	var r 			= Math.floor(Math.random()*41)-20;
	$content.stop().animate({
		'marginTop'		: '0px'
	},400).find('img').stop().animate({'rotate': r + 'deg'},400);
}

Следующая функция изменяет размер изображения так, чтобы оно соответствовало размеру окна:

//Функция изменения размеров на основании размеров окна
function resize($image){
	var widthMargin		= 50
	var heightMargin 	= 200;
					
	var windowH      = $(window).height()-heightMargin;
	var windowW      = $(window).width()-widthMargin;
	var theImage     = new Image();
	theImage.src     = $image.attr("src");
	var imgwidth     = theImage.width;
	var imgheight    = theImage.height;

	if((imgwidth > windowW)||(imgheight > windowH)){
		if(imgwidth > imgheight){
			var newwidth = windowW;
			var ratio = imgwidth / windowW;
			var newheight = imgheight / ratio;
			theImage.height = newheight;
			theImage.width= newwidth;
			if(newheight>windowH){
				var newnewheight = windowH;
				var newratio = newheight/windowH;
				var newnewwidth =newwidth/newratio;
				theImage.width = newnewwidth;
				theImage.height= newnewheight;
			}
		}
		else{
			var newheight = windowH;
			var ratio = imgheight / windowH;
			var newwidth = imgwidth / ratio;
			theImage.height = newheight;
			theImage.width= newwidth;
			if(newwidth>windowW){
				var newnewwidth = windowW;
				var newratio = newwidth/windowW;
				var newnewheight =newheight/newratio;
				theImage.height = newnewheight;
				theImage.width= newnewwidth;
			}
		}
	}
	$image.css({'width':theImage.width+'px','height':theImage.height+'px'});
}

Так как используем Cufon, то нужно добавить следующие строки в раздел head страницы HTML:

<script src="js/cufon-yui.js" type="text/javascript"></script>
<script src="js/EpsilonCTT_400.font.js" type="text/javascript"></script>
<script type="text/javascript">
	Cufon.replace('span');
	Cufon.replace('h1', {
		textShadow: '0px 1px #ddd'
	});
</script>

Готово!

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: tympanus.net/codrops/2010/09/28/polaroid-photobar-gallery/
Перевел: Сергей Фастунов
Урок создан: 9 Ноября 2010
Просмотров: 39080
Правила перепечатки


5 последних уроков рубрики "HTML и DHTML"

^ Наверх ^