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);
        }); 
});
Добавить комментарий