Как стать автором
Обновить

Анимация аккордеона и свойства height (max-height) в чистом CSS

Время на прочтение3 мин
Количество просмотров11K

Всем привет, недавно я изучил формы и анимации, после чего мне пришлось очень долго промучаться с анимацией Аккордеона и свойства max-height не прибегая к помощи Js в вычислениях, и сейчас я поделюсь с вами оптимальным решением.

Код на Codepen https://codepen.io/webfrontden/pen/poaOPER

P.S.: Java-sctipt использовался только для воздействия на класс (смены класса) , что можно сделать и с помощью псевдо классов css, например: Active.

document.getElementById("discount-entry__title-1").onclick = function () {
  document
    .getElementById("drop-up-body-discount-entry")
    .classList.toggle("drop-up-body_state_toggled");
  document
    .getElementById("discount-entry__title-1")
    .classList.toggle("title-summary_toggled");
};
document.getElementById("shipping-tax__title-1").onclick = function () {
  document
    .getElementById("drop-up-body-shipping-tax")
    .classList.toggle("drop-up-body_state_toggled");
  document
    .getElementById("shipping-tax__title-1")
    .classList.toggle("title-summary_toggled");
  document
    .getElementById("shipping-tax-1")
    .classList.toggle("shipping-tax_mb_disabled");
};
// Код js

Было перепробовано несколько вариантов с использованием отрицательного margin-top, position: absolute и transform: translate. Но во всех вариантах, обязательно, что бы контент не было видно за рамками родительского блока, добавьте его контейнеру свойство oveflow:hidden.

Код Css
Код Css
Код Html
Код Html

Итак, Margin-top недостаточно надежен т.к берет процентный отступ, от ширины родителя, а не его высоты. Заказчик рано или поздно может захотеть изменить блок, и в лучшем случае список будет просто быстрее заезжать наверх, из-за увеличения ширины блока, и соответственно отступа наверх, а в худшем не будет скрываться полностью, если высота станет слишком большой, а ширина не изменится. Анимация выглядит также, как и анимация через max-height, которую можно увидеть ниже

Transform: translate; адаптивен и плавно анимируется, но оставляет под собой пространство, которое нужно как-то убирать. Его можно убрать с помощью анимации, в конце которой, блок становиться абсолютно позиционированным и его становиться не видно т.к для родителя задан overflow:hidden;

/* При обычном состоянии */
.drop-up-body {
  transition: transform 1s;
  transform: translate(0, 0px);
  margin-top: 0;
}
/* При нажатии на кнопку */
.drop-up-body_state_toggled {
  transition: transform 1s;
  transform: translate(0, -110%);
  animation: position_hidden 0.00000000000000000001s 1s forwards;
}

@keyframes position_hidden {
  0% {
    position: static;
  }
  99% {
    position: static;
  }
  100% {
    position: absolute;
    top: -100%;
  }
}
Transform: translate; без анимации с position absolute
Transform: translate; без анимации с position absolute
Transform: translate; с анимацией position absolute
Transform: translate; с анимацией position absolute

Оптимальным вариантом будет свойство max-height, оно куда надежнее фиксированной высоты, и может подстраиваться под меньшее количество контента. Но и оно отказывалось работать до последнего, т.к я указывал начальное значение в процентах или оставлял max-height: auto - анимация не работала. Как оказалось при указании высоты в пикселях, все начинает работать. Значение в пикселях следует указывать с запасом как минимум в 2 раза, что добавит надежности вёрстке, так как если заказчик захочет изменить сайт и добавит контента в блок, что увеличит его высоту, у нас еще будет запас по max-height.

Единственное, что вам стоит уяснить, анимируется не текущее значение высоты блока, а разность высот между max-height в начале и после анимации. В моем случае значение max-height - 3000px, а высота контента примерно 550px. Таким образом, при сворачивании этого списка анимация начнется с задержкой, а именно, тогда когда анимация дойдет до значения высоты блока (в моем случае около 550px), т.к как сначала max-height снизится до этого числа, но блок не измениться, ведь его высота меньше max-height, а уже затем когда высота max-height, станет меньше высоты блока, блок начнет уменьшаться. При разворачивании max-height растет от нуля и изменения видны мгновенно.

Update: Если указать функцию анимации cubic-bezier(0, 1, 0, 1) к свойству transition: max-height 0.5s; Анимация будет плавной почти всегда . Это происходит потому что видимой анимации подвергаются только ее начало и конец, а остальное время анимация происходит чуть ли не мгновенно. Так в нашем случае невидимая часть блока с 3000 до 550px проанимируется очень быстро, а уже затем, пойдет видимая пользователю анимация (за совет спасибо @CyberGenius)

Визуализация работы функции анимации cubic-bezier(0, 1, 0, 1)
Визуализация работы функции анимации cubic-bezier(0, 1, 0, 1)
Анимация через max-height (Плавность регулируется через transition-duration)
Анимация через max-height (Плавность регулируется через transition-duration)

Я надеюсь, что мне удалось вам помочь!

Вот ссылка на Codepen еще раз https://codepen.io/webfrontden/pen/poaOPER

Если у вас остались вопросы, критика или комментарии - обязательно напишите мне, я с радостью отвечу вам.

А сейчас я прощаюсь и желаю вам хорошего дня!

Денис

Теги:
Хабы:
+1
Комментарии15

Публикации

Изменить настройки темы

Истории

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн