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

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

Код JS
Код JS

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

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

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

Transform: translate;, адаптивен и плавно анимируется, но оставляет под собой пространство. Если добавить к этому свойству position: absolute с задержкой (transition: transform 1s, position 1s 1s), то пустое пространство исчезнет, но мгновенно, сразу после конца анимации, т.к position не анимируется. Это создаст неприятное ощущение рванной анимации.

Transform: translate без position absolute
Transform: translate без position absolute
Transform: translate c position: absolute через задержку
Transform: translate c 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 растет от нуля и изменения видны мгновенно.

До аннимации
До аннимации
После аннимации
После аннимации

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

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

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

Денис

Комментарии (11)


  1. p0rsche
    02.06.2022 16:47
    +6

    Было бы неплохо добавить ссылку на какой-нибудь jsfiddle и гифку с анимацией, как это работает.


    1. webfrontden Автор
      02.06.2022 19:41
      +1

      Добрый вечер, большое cпасибо за критику, до 10 июня обновлю статью и добавлю ссылку на песочницу кода + гифку.

      P.S К слову, такой долгий срок обусловлен тем, что на момент написания кода не думал, что буду выкладывать его в песочницу. Но сейчас, немного почищу код, добавлю комментарии, чтобы читать его было проще, а потом выложу его в песочницу.


  1. Turba
    02.06.2022 19:41
    +2

    Единственный вариант, в котором анимация через max-height будет смотреться корректно, это когда известен размер анимируемого блока. А вот так анимировать, я бы не советовал. Будет выглядеть неровно/некрасиво/убого, особенно если вы применяете не линейную функцию плавности.

    Альтернатива - это анимация колонки на гридах. Здесь, кажется, уже где-то проскакивало. К сожалению, из-за бага поддержка этого метода оставляет желать лучшего.


    1. webfrontden Автор
      02.06.2022 19:51
      +1

      Да, соглашусь с тем, что анимация через max-height имеет свои недостатки, и если я найду более оптимальное решение, то поделюсь им. Однако, кроме известного размера блока, дёрганость можно решить микроанимациями (0.3 и 0.2 s), что не дает ее увидеть, а ощущение плавности для пользователя всё же создастся.

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


    1. CyberGenius
      03.06.2022 08:34

      Спешу вас порадовать. Через max-height, даже без заранее известного размера блока, можно добиться плавности. Добавьте cubic-bezier

      .text { overflow: hidden; max-height: 0; transition: max-height 0.5s cubic-bezier(0, 1, 0, 1); &.full { max-height: 1000px; transition: max-height 1s ease-in-out; }


      1. webfrontden Автор
        03.06.2022 09:50

        Спасибо, протестирую, возможно добавлю в статью


  1. Hystrider
    02.06.2022 21:44
    +1

    Да, у этого метода есть недостаток с различным временем анимации.

    Я тоже в свое время кучу экспериментов перепробовал, но всетаки остановился на анимации height. Т.е. все как и в статье overflow:hidden, height:0 и transition на height.

    А потом просто по клику в скрипте:

    elem.style.height = `${elem.scrollHeight}px`;

    что добавляет inline стиль height с актуальной высотой элемента и за счет css перехода, он анимируется. И при закрытии соответственно.

    elem.style.height="";

    это убирает inline стиль, и элемент плавно сворачивается.

    Кажется, это самое простое решение.


    1. PaulZi
      05.06.2022 02:41

      Тут возникает другая проблема, что высота контента может поменяться (например вследствие изменении размера окна), и тогда контент вылезет за зафиксированную скриптом высоту. Со скриптами можно уже и нормально реализовать. Навесив на transitionend - height: auto.


  1. moirabrown
    03.06.2022 09:50

    Метод неплохой, но ещё есть потенциальная проблема, если высота контента внутри аккордеона превысит высоту, которая у вас указана в max-height и содержимое начнет "вываливаться" из него


    1. webfrontden Автор
      03.06.2022 09:52

      Именно поэтому в моем случае высота контента около 550px, а высота max-height 3000px. Да такая разница в высоте создает небольшую задержку, но добавляет надежности, ведь я не думаю, что заказчик увеличит блок с формой в 4 с половиной раза

      P.S Благодаря вашему комментарию, добавил информацию об указании высоты с запасом в статью, чтобы любой, без чтения комментариев мог узнать про это. Спасибо


  1. PavPavel
    03.06.2022 19:49

    Интересная тема. Вопрос следующий: анимация hight на странице перерисовывает полностью всю страницу (F12 > Console > Ecs > Rendering > Paint Flashing). Как реализовать аккордеон с плавной анимацией без перерисовки макета? (может быть использовать opacity, transform ...)