Статьи

Создание фильтрующего компонента с помощью CSS-анимации и jQuery

Несколько месяцев назад я написал статью о MixItUp , популярном плагине jQuery для фильтрации и сортировки. В сегодняшней статье я покажу вам, как создать свой собственный простой фильтруемый компонент с анимацией jQuery и CSS.

Без лишних слов, давайте начнем!

Настройка HTML

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

<div class="cta filter">
  <a class="all active" data-filter="all" href="#" role="button">Show All</a>
  <a class="green" data-filter="green" href="#" role="button">Show Green Boxes</a>
  <a class="blue" data-filter="blue" href="#" role="button">Show Blue Boxes</a>
  <a class="red" data-filter="red" href="#" role="button">Show Red Boxes</a>
</div>

<div class="boxes">
  <a class="red" data-category="red" href="#">Box1</a>
  <a class="green" data-category="green" href="#">Box2</a>
  <a class="blue" data-category="blue" href="#">Box3</a>

 <!-- other anchor/boxes here ... -->

</div>

Обратите внимание, что я установил довольно простую разметку. Вот объяснение этого:

  • Сначала я определил кнопки фильтра и элементы, которые я хочу фильтровать (мы назовем их целевыми элементами).
  • Затем я сгруппировал целевые элементы в три категории (синий, зеленый и красный) и дал им атрибут data-category Значение этого атрибута определяет категорию, к которой принадлежит каждый элемент.
  • Я также назначил атрибут фильтра данных для кнопок фильтра. Значение этого атрибута указывает желаемую категорию фильтра. Например, кнопка с атрибутом / значением data-filterdata-filter="red" С другой стороны, кнопка с red

Теперь, когда у вас есть обзор необходимого HTML, мы можем перейти к изучению CSS.

Настройка CSS

Каждый раз, когда категория фильтра активна, соответствующая кнопка фильтра получает data-filter="all" По умолчанию кнопка с атрибутом active

Коробка с активным классом

Вот связанные стили:

 data-filter="all"

Кроме того, я собираюсь использовать flexbox для создания макета для целевых элементов.

Использование flexbox для макета

Смотрите связанные стили ниже:

 .filter a {
  position: relative;
}

.filter a.active:before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  display: inline-block;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 15px 15px 0 0;
  border-color: #333 transparent transparent transparent;
}

Наконец, я определяю две разные CSS-анимации ключевых кадров, которые я буду использовать позже для раскрытия элементов:

 .boxes {
  display: flex;
  flex-wrap: wrap;
}

.boxes a {
  width: 23%;
  border: 2px solid #333;
  margin: 0 1% 20px 1%;
  line-height: 60px;
}

Имея разметку и CSS, мы можем начать создавать JavaScript / jQuery.

Настройка jQuery

Посмотрите на код ниже, после чего я объясню, что происходит:

 @keyframes zoom-in {
  0% {
   transform: scale(.1);
  }
  100% {
    transform: none;
  }
}

@keyframes rotate-right {
  0% {
    transform: translate(-100%) rotate(-100deg);
  }
  100% {
    transform: none;
  }
}

.is-animated {
  animation: .6s zoom-in;
  // animation: .6s rotate-right; 
}

При каждом нажатии кнопки фильтра происходит следующее:

  • var $filters = $('.filter [data-filter]'),
    $boxes = $('.boxes [data-category]');

    $filters.on('click', function(e) {
    e.preventDefault();
    var $this = $(this);
    $filters.removeClass('active');
    $this.addClass('active');

    var $filterColor = $this.attr('data-filter');

    if ($filterColor == 'all') {
    $boxes.removeClass('is-animated')
    .fadeOut().promise().done(function() {
    $boxes.addClass('is-animated').fadeIn();
    });
    } else {
    $boxes.removeClass('is-animated')
    .fadeOut().promise().done(function() {
    $boxes.filter('[data-category = "' + $filterColor + '"]')
    .addClass('is-animated').fadeIn();
    });
    }
    });

  • Значение атрибута active
  • Если значение data-filterdata-filter Для этого я сначала скрываю их, а затем, когда все элементы становятся скрытыми, показываю их с помощью CSS-анимации с allrotate-right
  • Если значение не zoom-in Для этого я сначала скрываю все элементы, а затем, когда все они становятся скрытыми, я показываю только элементы соответствующей категории, используя CSS-анимацию с allrotate-right

На данный момент важно уточнить одну вещь. Обратите внимание на синтаксис метода zoom-in Это выглядит следующим образом:

 fadeOut()

Вы, вероятно, более знакомы с этим синтаксисом, хотя:

 $boxes.fadeOut().promise().done(function() {
  // callback's body
});

Эти декларации имеют разные значения:

  • В первом случае обратный вызов выполняется только после того, как все целевые элементы становятся скрытыми. Вы можете узнать больше информации об этом подходе, посетив раздел обещания () документации jQuery.
  • Во втором случае обратный вызов запускается несколько раз. В частности, он выполняется каждый раз, когда элемент становится скрытым.

Демонстрация ниже использует анимацию $boxes.fadeOut(function() {
// callback's body
});

И эта демонстрация использует анимацию zoom-in

Конечно, вышеупомянутые CSS-анимации не являются обязательными. Если вам не нравятся эти конкретные анимации, не стесняйтесь удалять их и открывать элементы, используя только метод rotate-right

Теперь, когда вы понимаете, как работает компонент, я покажу вам, как создать его другой вариант.

Анимация элементов последовательно

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

 fadeIn()

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

  • Во-первых, я использую метод each () для перебора целевых элементов. Плюс, когда он зацикливается, я получаю индекс текущего элемента (который начинается с нуля) и умножаю его на число (например, 200). Полученное число передается в качестве аргумента методу $filters.on('click', function(e) {

    // same code as above here

    if ($filterColor == 'all') {
    $boxes.removeClass('is-animated')
    .fadeOut().finish().promise().done(function() {
    $boxes.each(function(i) {
    $(this).addClass('is-animated').delay((i++) * 200).fadeIn();
    });
    });
    } else {
    $boxes.removeClass('is-animated')
    .fadeOut().finish().promise().done(function() {
    $boxes.filter('[data-category = "' + $filterColor + '"]').each(function(i) {
    $(this).addClass('is-animated').delay((i++) * 200).fadeIn();
    });
    });
    }
    });
    Это число указывает количество миллисекунд, которые каждый элемент должен ждать, прежде чем исчезнуть.

  • Затем я использую метод finish (), чтобы остановить текущую анимацию для выбранных элементов в определенных случаях. Чтобы понять его использование, вот сценарий: нажмите кнопку фильтра, а затем, прежде чем появятся все элементы, нажмите кнопку еще раз. Вы заметите, что все элементы исчезают. Аналогично, запустите этот тест снова после удаления двух экземпляров этого метода. В таком случае вы увидите, что элементы получают нежелательные эффекты. Иногда правильно вызвать этот метод может быть сложно. Для этого примера мне пришлось немного поэкспериментировать, пока я не нашел, где мне его разместить.

Демонстрация ниже анимирует отфильтрованные элементы последовательно, используя анимацию delay

Демонстрация ниже анимирует отфильтрованные элементы последовательно, используя анимацию zoom-in

Вывод

Этот же компонент может быть построен без jQuery и может иметь лучшую производительность, но возможность использовать rotate-rightfadeIn()fadeOut()

Дайте мне знать в комментариях, если у вас есть другое решение или способ улучшить код.