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

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

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

Начнем!

Разметка

Мы собираемся включить весь наш 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>


Готово!

  • FalleN

  • 1481

  • 1

  • 185

Ссылки на статью:

Похожие статьи: