CSS3-эффект «колоды карт» для изображений

Будь в курсе

subscrible
Скидка 5%
по подписке
Автор
Куликов
Александр
веб-программист
Все великое начинается с малого

Довольно часто в интернете можно встретится с интересными эффектами на сайте и многие из них нацелены на то, чтобы сфокусировать на себе интерес пользователя и задержать его на сайте как можно дольше (Youtube: «10 часов с гипно-жабой»).

Увеличение среднего времени на сайте делает сайт более привлекательным для рекламодателей и хорошо влияет на ранжирование в поисковых системах.

Идея

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

Идея эффекта в том, чтобы прокручивая страницу изображения оставляли за собой плавный «хвост» из своих копий, будто сдвинута колода карт.

ГОТОВЫЙ ПРИМЕР

Как это работает

Чтобы реализовать данный эффект необходимо было сперва создать несколько копий изображений и поместить их под главное изображение. Для этого используем метод .clone(), и забегая вперед скажу, что нам понадобится знать количество изображений поэтому мы объявим один раз переменную для количества изображений и будем ее использовать. Создаем простейший цикл создания копий изображений:

 var img_count = 4; $('.img-block').each(function(){ for (i = 1; i <= img_count; i++) { $(this).find('img:last-child') .clone() .addClass("newImg"+i) .prependTo(this); }}); 

 

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

img[class^="newImg"]{ position: absolute; transition: 0.2s; } 

 

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

 function moveBtm(img_count, current){ for (var i = 2; i <= img_count; i++) { j = i+i; $('.newImg'+i).css('transform','translate3d(0, '+current*j+'px, 0)'); } } 

 

В функцию добавляем цикл обхода каждого изображения. В смещении будем использовать разницу положения страницы от начала скроллинга до конца, за это отвечает переменная current. Таким образом мы получим разное расстояние смещения при разной силе скролла. А чтобы еще больше усилить эффект смещения мы умножим его на порядковый номер изображения, таким образом картинки не будут складываться в единый блок и будут смещаться друг за другом. С помощью такой манипуляции j = i+i (где i — порядковый номер изображения) мы можем регулировать отступы картинок друг за другом и они будут иметь визуально правильную разницу отступа. Цикл стартуем со второго изображения, поскольку нам необходимо, чтобы первая картинка была неподвижной.

 

Тоже самое делаем и для скролла вниз:

function moveTop(img_count, current){ for (var i = 2; i <= img_count; i++) { j = i+i; $('.newImg'+i).css('transform','translate3d(0, -'+current*j+'px, 0)'); } } 

 

С той лишь разницей, что по оси Y мы делаем смещение в минус.

Далее нам необходимо сбросить стиль translate3d иначе картинки не будут возвращаться назад до конца:

function clear(img_count){ for (var i = 2; i <= img_count; i++) { $('.newImg'+i).css('transform','translate3d(0, 0, 0)'); } }

Проблемы

Итак, картинки созданы, смещение задано, теперь настало время для задания действий. В целом уже сейчас можно слушать обычный scroll() и с помощью scrollTop() вычислить наш current, но проблема в том, что стандартный скроллинг слишком грубый и не позволяет нам добиться плавности при прокрутке.

К тому же при таком скроллинге картинки будут грубо скакать и добиться плавности будет очень сложно.

Решение с помощью js плагинов

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

var Scrollbar = window.Scrollbar; Scrollbar.use(window.OverscrollPlugin) const scrollbar = Scrollbar.init(document.querySelector('body'), { damping: 0.08, plugins: { overscroll: { effect: 'bounce', } }, });

 

После подключения плагина Smooth Scrollbar стандартный метод scroll() будет выдавать нам пустое значение, это особенность работы плагина, но разработчики плагина продумали это и добавили собственный метод для прослушивания скролла. Добавляем переменную для записи в нее значения текущей позиции страницы и слушаем дальнейшие действия пользователя с помощью метода scrollbar.addListener():

CurrentScroll = 0; scrollbar.addListener(() => { var offset = scrollbar.offset.y; var NextScroll = offset; if ((NextScroll > CurrentScroll) && (offset != 0)){ current = NextScroll - CurrentScroll; moveBtm(img_count, current); } else if (offset != 0) { current = CurrentScroll - NextScroll; moveTop(img_count, current); } CurrentScroll = NextScroll; setTimeout(function(){ clear(img_count); }, 400); });

 

Поскольку scroll() не работает, то в плагине также присутствует метод для отслеживания позиции страницы scrollbar.offset. С его помощью вычисляем наш current по простой формуле:

 current = NextScroll - CurrentScroll - если крутим вниз current = CurrentScroll - NextScroll - если крутим вверх 

 

Чтобы понять в какую сторону скроллится страница просто сравниваем прошлое положение и новое:

 если NextScroll > CurrentScroll - то крутится вниз, иначе вверх 

 

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

$(document).ready(function(){ var img_count = 4; $('.img-block').each(function(){ for (i = 1; i <= img_count; i++) { $(this).find('img:last-child').clone().addClass("newImg"+i).prependTo(this); } }); function moveBtm(img_count, current){ for (var i = 2; i <= img_count; i++) { j = i+i; $('.newImg'+i).css('transform','translate3d(0, '+current*j+'px, 0)'); } } function moveTop(img_count, current){ for (var i = 2; i <= img_count; i++) { j = i+i; $('.newImg'+i).css('transform','translate3d(0, -'+current*j+'px, 0)'); } } function clear(img_count){ for (var i = 2; i <= img_count; i++) { $('.newImg'+i).css('transform','translate3d(0, 0, 0)'); } } var Scrollbar = window.Scrollbar; Scrollbar.use(window.OverscrollPlugin) const scrollbar = Scrollbar.init(document.querySelector('body'), { damping: 0.08, plugins: { overscroll: { effect: 'bounce', } }, }); CurrentScroll = 0; scrollbar.addListener(() => { var offset = scrollbar.offset.y; var NextScroll = offset; if ((NextScroll > CurrentScroll) && (offset != 0)){ current = NextScroll - CurrentScroll; moveBtm(img_count, current); } else if (offset != 0) { current = CurrentScroll - NextScroll; moveTop(img_count, current); } CurrentScroll = NextScroll; setTimeout(function(){ clear(img_count); }, 400); }); });