Портфолио с использованием jQuery и CSS3

Здравствуйте, уважаемые читатели XoZbloga! Существует много способов отображения портфолио с эскизами выполненных работ, один из них — это упорядоченные плитки с псевдо-постраничной навигацией. В этом уроке я расскажу о том, как можно изящно реализовать подобное решение с использованием JQuery и CSS3 Transform.

Концепция

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

Плитка эскизов в портфолио

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

Эффекты вращения

HTML разметка

Мы создадим список изображений с использованием списка ul и пустой блок div (#portfolio) для отображения «текущей партии» эскизов, HTML разметка которой будет формироваться в JavaScript. Список со всеми изображениями будет скрыт:

1
2
3
4
5
6
7
8
<div id="portfolio"></div>

<ul id="portfolio-item">
  <li><img src="images/dumptruck_teaser.jpeg" alt="Dump Truck"></li>
  <li><img src="images/rrwooo_rrwooo_teaser.jpeg" alt="Rrwooo"></li>
  <li><img src="images/dozer_teaser.jpeg" alt="Dozer"></li>
  <!-- остальные изображения -->
</ul>

Разметка пустого блока div (#portfolio) будет формироваться в JavaScript следующим образом:

1
2
3
4
5
<div id="portfolio">
  <div style="background:url(images/dumptruck_teaser.jpeg)"> <span>Dump Truck</span></div>
  <div style="background:url(images/rrwooo_rrwooo_teaser.jpeg)"> <span>Rrwooo</span></div>
  <!-- остальные блоки -->
</div>

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

Текущие эскизы будут выстраиваться в 2 ряда блоков div и находиться в области 3D-пространства, так как являются дочерними элементами блока #portfolio, имеющего свойство transform-style: preserve-3d. Список со всеми изображениями, как уже указывалось выше, скрыт display: none;.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#portfolio-item {
  display: none;
}
 
#portfolio {
  margin: 2em 0 1em;
  transform-style: preserve-3d;
}

 #portfolio div {
   display: inline-block;
   position: relative;
   vertical-align: top;
   margin: 1em;
   width: 200px;
   height: 150px;
   box-shadow: 0px 0px 25px rgba(0,0,0,.3);
 }
 
   #portfolio div span {
     text-align: left;
     position: absolute;
     color: #fff;
     background: rgba(0,0,0,0.6);
     width: 90px;
     padding: 5px 15px 7px;
     z-index: 1;
     left: -10px;
     bottom: 10px;
   }

Теперь создадим классы для анимации блоков:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.animated {
  transition: .9s ease-out;
}
 
.flipped-horizontal-right {
  transform: rotateY(360deg);
}
 
.flipped-horizontal-left {
  transform: rotateY(-360deg);
}
 
.flipped-vertical-top {
  transform: rotateX(360deg);
}
 
.flipped-vertical-bottom {
  transform: rotateX(-360deg);
}

Портфолио

jQuery

Первое инициализируем некоторые переменные, которые нам пригодятся при формировании текущего списка эскизов. После чего сформируем для каждого из эскизов HTML каркас:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Количество текущих эскизов
itemShow = 8;
// Максимальный индекс
indexedItemShow  = itemShow - 1
// Область (список ul) из которой выбираются изображения
itemList = $('#portfolio-item');
// Область куда помещаются текущие эскизы
itemWrapper = $('#portfolio');
// Массив с классами вариантов анимации
rotation = ['flipped-vertical-bottom', 'flipped-vertical-top', 'flipped-horizontal-left', 'flipped-horizontal-right'];
// Область навигации
navigation = $('#navigation a');

// Формируем каркас
for( var i = 0; i < itemShow; i++ ) {
  // Выбираем изображение из области
  itemImage = itemList.children('li:eq(' + i + ')').children('img');
  // Получаем значение атрибута src (путь до картинки)
  itemSrc = itemImage.attr('src');
  // Получаем значение атрибута alt (описание)
  itemAlt = itemImage.attr('alt');
  // Формируем блок div
  item = '<div style="background:url(' + itemSrc + ')"> <span>' + itemAlt + '</span></div>';
  itemWrapper.append(item);
}

При клике по кнопкам навигации случайным образом Math.random() происходит выбор одного из вариантов анимации, посредством выбора из массива одного из классов.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
navigation.on('click', function(e) {
  // Отменяем стандартное действие  
  e.preventDefault();
  // Работа с кнопками навигации
  navigation.removeClass('selected');
  $(this).addClass('selected');
  // Получаем номер нажатой кнопки  
  page = $(this).attr('data-page');
  // Запускаем цикл
  for( var i = 0; i <= indexedItemShow; i++ ) {
    // Рандомно выбираем эффект анимации
    random     = Math.floor( Math.random() * ( 3 - 0 + 1 ) );
    animation  = rotation[random];
    // Выбираем текущий div  
    item       = itemWrapper.children('div:eq(' + i + ')');
    // Присваиваем класс анимации
    item.addClass('animated ' + animation);
 
    /* следующий код */
  }
});

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
window.setTimeout(function (index) {
   return function () {
     // Вычисляем индекс
     indexReal    = (page == 1) ? index : (index + (page - 1)) ;
     itemHost     = indexReal + (indexedItemShow * (page - 1));
     // Получаем данные
     itemImage    = itemList.children('li:eq(' + itemHost + ')').children('img');
     itemSrc      = itemImage.attr('src');
     itemAlt      = itemImage.attr('alt');
     itemCurrent  = itemWrapper.children('div:eq(' + index + ')');
     // Используем полученные данные
     itemCurrent.css('background', 'url(' + itemSrc + ')');
     itemCurrent.children('span').text(itemAlt);
   };
} (i), 500);

Когда анимация закончится, мы удаляем анимационный эффект и имя класса для каждого блока.

1
2
3
item.on('transitionend webkitTransitionEnd MSTransitionEnd oTransitionEnd', function() {
    $(this).removeClass();
});

На этом все! Посетите демонстрационную страницу (кнопка в начале урока), чтобы увидеть портфолио в живую.

Чтобы оставаться в курсе свежих статей и уроков подписывайтесь на еженедельную почтовую рассылку или на новостную ленту RSS. Спасибо!