Анимация аккордеонов на JavaScript была одной из самых популярных анимаций на веб-сайтах. Интересный факт: функция jQuery slideDown ()
уже была доступна в первой версии в 2006 году.
В этой статье мы увидим, как можно анимировать собственный элемент
Содержание статьи
Настройка HTML
Во-первых, давайте посмотрим, как мы будем структурировать разметку, необходимую для этой анимации.
Для элемента
. Резюме — это содержимое, видимое, когда аккордеон закрыт. Все остальные элементы в
являются частью внутреннего содержания аккордеона. Чтобы упростить анимацию этого контента, мы помещаем его в
.
Краткое содержание аккордеона
Lorem, ipsum dolor sit amet conctetur adipisicing elit.
Modi unde, ex rem voluptates autem aliquid veniam quis temporibus repudiandae illo, nostrum, pariatur quae!
At animi modi dignissimos correpti placeat voluptatum!
Аккордеонный класс
Чтобы сделать наш код более многоразовым, мы должны создать класс Accordion
. Таким образом мы можем вызывать новый Accordion ()
для каждого элемента
на странице.
класс Accordion {
// Конструктор по умолчанию для каждого аккордеона
конструктор () {}
// Функция вызывается, когда пользователь нажимает на сводку
по щелчку() {}
// Функция вызывается для закрытия содержимого с анимацией
сокращаться, сжиматься() {}
// Функция вызывается для открытия элемента после щелчка
открытым() {}
// Функция вызывается для расширения содержимого анимацией
expand () {}
// Обратный вызов после завершения анимации сжатия или расширения
onAnimationFinish () {}
}
Конструктор ()
Конструктор — это место, где мы сохраняем все данные, необходимые для аккордеона.
конструктор (эл) {
// Сохраняем элемент
this.el = el;
// Сохраняем элемент
this.summary = el.querySelector ('сводка');
// Сохраняем элемент
this.content = el.querySelector ('. content');
// Сохраняем объект анимации (чтобы при необходимости можно было его отменить)
this.animation = null;
// Сохраняем, если элемент закрывается
this.isClosing = false;
// Сохраняем, если элемент расширяется
this.isExpanding = false;
// Обнаружение нажатия пользователем на элемент сводки
this.summary.addEventListener ('щелкните', (e) => this.onClick (e));
}
onClick ()
В функции onClick ()
вы заметите, что мы проверяем, анимируется ли элемент (закрывается или расширяется). Нам нужно это сделать, если пользователи нажимают на аккордеон во время его анимации. В случае быстрых щелчков мы не хотим, чтобы аккордеон перескакивал из полностью открытого состояния в полностью закрытое.
Элемент
имеет атрибут [open]
применяемый к нему браузером, когда мы открываем элемент. Мы можем получить значение этого атрибута, проверив свойство open
нашего элемента с помощью this.el.open
.
onClick (e) {
// Остановить поведение по умолчанию из браузера
e.preventDefault ();
// Добавить переполнение на чтобы избежать переполнения содержимого
this.el.style.overflow = 'скрытый';
// Проверяем, закрывается ли элемент или уже закрыт
if (this.isClosing ||! this.el.open) {
this.open ();
// Проверяем, открывается ли элемент или уже открыт
} else if (this.isExpanding || this.el.open) {
this.shrink ();
}
}
сжатие ()
Эта функция сжатия использует функцию WAAPI .animate ()
. Вы можете прочитать об этом больше в документации MDN. WAAPI очень похож на CSS @keyframes
. Нам нужно определить начальный и конечный ключевые кадры анимации. В этом случае нам нужны только два ключевых кадра, первый — это текущая высота элемента, а второй — высота элемента
после его закрытия. Текущая высота сохраняется в переменной startHeight
. Замкнутая высота сохраняется в переменной endHeight
и равна высоте
.
сжать () {
// Установить элемент как «закрывающийся»
this.isClosing = true;
// Сохраняем текущую высоту элемента
const startHeight = `$ {this.el.offsetHeight} px`;
// Рассчитываем высоту резюме
const endHeight = `$ {this.summary.offsetHeight} px`;
// Если анимация уже запущена
if (this.animation) {
// Отменить текущую анимацию
this.animation.cancel ();
}
// Запускаем WAAPI-анимацию
this.animation = this.el.animate ({
// Устанавливаем ключевые кадры от startHeight до endHeight
высота: [startHeight, endHeight]
}, {
// Если продолжительность слишком медленная или быстрая, вы можете изменить ее здесь
продолжительность: 400,
// Вы также можете изменить легкость анимации
ослабление: 'легкость выхода'
});
// Когда анимация завершена, вызываем onAnimationFinish ()
this.animation.onfinish = () => this.onAnimationFinish (ложь);
// Если анимация отменена, переменной isClosing устанавливается значение false
this.animation.oncancel = () => this.isClosing = false;
}
открытый ()
Функция open
вызывается, когда мы хотим расширить аккордеон. Эта функция пока не управляет анимацией аккордеона. Сначала мы вычисляем высоту элемента
и применяем эту высоту со встроенными стилями к нему. Как только это будет сделано, мы можем установить для него атрибут open, чтобы сделать содержимое видимым, но скрытым, поскольку у нас есть overflow: hidden
и фиксированная высота элемента. Затем мы ждем, пока следующий кадр вызовет функцию расширения и анимирует элемент.
открыть () {
// Применяем фиксированную высоту к элементу
this.el.style.height = `$ {this.el.offsetHeight} px`;
// Принудительное использование атрибута [open] в элементе сведений
this.el.open = true;
// Ждем, пока следующий кадр вызовет функцию расширения
window.requestAnimationFrame (() => this.expand ());
}
развернуть ()
Функция расширения аналогична функции сжатия
но вместо анимации от текущей высоты до ближайшей высоты мы анимируем от высоты элемента до конечной высоты. Эта конечная высота равна высоте резюме плюс высота внутреннего содержимого.
развернуть () {
// Установить элемент как «расширяющийся»
this.isExpanding = true;
// Получить текущую фиксированную высоту элемента
const startHeight = `$ {this.el.offsetHeight} px`;
// Вычислить открытую высоту элемента (суммарная высота + высота содержимого)
const endHeight = `$ {this.summary.offsetHeight + this.content.offsetHeight} px`;
// Если анимация уже запущена
if (this.animation) {
// Отменить текущую анимацию
this.animation.cancel ();
}
// Запускаем WAAPI-анимацию
this.animation = this.el.animate ({
// Устанавливаем ключевые кадры от startHeight до endHeight
высота: [startHeight, endHeight]
}, {
// Если длительность слишком низкая или быстрая, вы можете изменить ее здесь
продолжительность: 400,
// Вы также можете изменить легкость анимации
ослабление: 'легкость выхода'
});
// Когда анимация завершена, вызываем onAnimationFinish ()
this.animation.onfinish = () => this.onAnimationFinish (true);
// Если анимация отменена, для переменной isExpanding устанавливается значение false
this.animation.oncancel = () => this.isExpanding = false;
}
onAnimationFinish ()
Эта функция вызывается в конце анимации сжатия или расширения. Как видите, есть параметр [open]
для которого установлено значение true, когда аккордеон открыт, что позволяет нам установить атрибут HTML [open]
для элемента , поскольку он больше не обрабатывается браузером.
onAnimationFinish (open) {
// Устанавливаем открытый атрибут на основе параметра
this.el.open = open;
// Очистить сохраненную анимацию
this.animation = null;
// Сбросить isClosing и isExpanding
this.isClosing = false;
this.isExpanding = false;
// Удаляем скрытое перетекание и фиксированную высоту
this.el.style.height = this.el.style.overflow = '';
}
Настройте аккордеоны
Уф, мы закончили с большей частью кода!
Все, что нам осталось, — это использовать наш класс Accordion
для каждого элемента
в HTML. Для этого мы используем querySelectorAll
в теге
и создаем новый экземпляр Accordion
для каждого из них.
]
document.querySelectorAll ('подробности'). ForEach ((el) => {
новый Аккордеон (эл);
});
Примечания
Чтобы произвести расчеты закрытой и открытой высоты, нам нужно убедиться, что
и содержимое всегда имеют одинаковую высоту.
Например, не пытайтесь добавлять отступы к сводке, когда она открыта, поскольку это может привести к скачкам во время анимации. То же самое и с внутренним содержимым — оно должно иметь фиксированную высоту, и нам следует избегать содержимого, которое может изменить высоту во время анимации открытия.
Кроме того, не добавляйте поле между сводкой и содержимым, поскольку оно не будет рассчитываться для ключевых кадров высоты. Вместо этого используйте отступы непосредственно в содержимом, чтобы добавить некоторый интервал.
Конец
И вуаля у нас есть красивый анимированный аккордеон на JavaScript без какой-либо библиотеки! 🌈