Bi-directional CSS height animation

Viewed 147

@keyframes mgm {
  from {
    max-height: 250px;
  }
  
  to {
    max-height: 0px;
  }
}
  
.mgm {
    width: 180px;
    border: 1px solid black;
    padding: 10px;
    animation: mgm 1s ease-in-out;
    max-height: 250px;
    overflow:hidden;
}
<div class="mgm">
  Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae, 
  blanditiis qui porro possimus commodi laudantium voluptatum accusantium. 
  Maxime alias dolorum quo aliquam delectus qui illo officiis, consequuntur 
  asperiores fugiat ducimus!
</div>

By running above code, height of the content is decreasing from bottom only and animation stops at the top. But I want to decrease the height from both bottom and top equally i.e; animation should stop at the centre of the content.

How to achieve this ?

Alternate approach - Yes we can do this by using scaleY CSS property but it shrinks the internal content. As given below -

@keyframes mgm {
  from {
    transform:scaleY(1);
  }
  
  to {
    transform:scaleY(0);
  }
}
  
.mgm {
    width: 180px;
    border: 1px solid black;
    padding: 10px;
    animation: mgm 1s ease-in-out;
    max-height: 250px;
    overflow:hidden;
}
<div class="mgm">
  Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae, 
  blanditiis qui porro possimus commodi laudantium voluptatum accusantium. 
  Maxime alias dolorum quo aliquam delectus qui illo officiis, consequuntur 
  asperiores fugiat ducimus!
</div>

3 Answers

@dommmm's Answer is also correct. In case if you don't want to play with positioning you can achieve it with flex as well. This also has same approach of wrapping the animated div in a container. Here the height is fixed to 250px (same as the max height of the animated div) to avoid page scrolling. And then the animated div is positioned to center. I've also reduced padding top and bottom from 10px to 0px to achieve the div closing completely.

.animation-container {
      display: flex;
      align-items: center;
      height: 250px;
    }

@keyframes mgm {
  from {
    max-height: 250px;
    padding: 10px 10px;
  }
  to {
    max-height: 0px;
    padding: 0px 10px;
  }
}

.animation-container {
  display: flex;
  align-items: center;
  height: 250px;
}

.mgm {
  width: 180px;
  border: 1px solid black;
  padding: 10px;
  animation: mgm 1s ease-in-out alternate infinite;
  max-height: 250px;
  overflow: hidden;
  display: flex;
  align-items: center;
}
<div class="animation-container">
  <div class="mgm">
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae, blanditiis qui porro possimus commodi laudantium voluptatum accusantium. Maxime alias dolorum quo aliquam delectus qui illo officiis, consequuntur asperiores fugiat ducimus!
  </div>
</div>

To achieve this effect you will need to place the text in a child container and position it absolutely. Shrinking the parent div in this manner will not shrink the internal content.

@keyframes mgm {
  from {
    transform: translateY(0%);
    height: 150px; /* original container height */
  }

  to {
    transform: translateY(75px); /* 50% of original container height */
    height: 0px;
  }
}

.mgm {
    width: 50%;
    border: 1px solid black;
    animation: mgm 1s ease-in-out;
    height: 150px;
    overflow: hidden;
    position: relative;
}

.mgm p {
  position: absolute;
  margin: 10px;
  top: 50%;
  transform: translateY(-50%);
  max-height: 150px;
  max-width: 100%;
}
<div class="mgm">
  <p>
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae, 
    blanditiis qui porro possimus commodi laudantium voluptatum accusantium. 
    Maxime alias dolorum quo aliquam delectus qui illo officiis, consequuntur 
    asperiores fugiat ducimus!
  </p>
</div>

Another idea using clip-path where you don't need to deal with height

@keyframes mgm {
  to {
    clip-path: inset(50% 0);
  }
}

.mgm {
  width: 180px;
  border: 1px solid black;
  padding: 10px;
  animation: mgm 1s ease-in-out infinite alternate;
  clip-path: inset(0 0);
}
<div class="mgm">
  Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae, blanditiis qui porro possimus commodi laudantium voluptatum accusantium. Maxime alias dolorum quo aliquam delectus qui illo officiis, consequuntur asperiores fugiat ducimus!
</div>

To get the border effect, you can consider a pseudo element like below:

.mgm {
  width: 180px;
  padding: 10px;
  animation: mgm 1s linear infinite alternate;
  clip-path: inset(0 0);
  position: relative;
}

@keyframes mgm {
  to {
    clip-path: inset(50% 0);
  }
}

.mgm:before {
  content: "";
  position: absolute;
  border: 1px solid black;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 1px 0;
  animation: inherit;
  animation-name: mgm-b;
}

@keyframes mgm-b {
  to {
    margin: 50% 0;
  }
}
<div class="mgm">
  Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae, blanditiis qui porro possimus commodi laudantium voluptatum accusantium. Maxime alias dolorum quo aliquam delectus qui illo officiis, consequuntur asperiores fugiat ducimus!
</div>

The pseudo element can be enough if you don't want transparency:

.mgm {
  width: 180px;
  padding: 10px;
  position: relative;
  overflow:hidden;
}

.mgm:before {
  content: "";
  position: absolute;
  border: 1px solid black;
  box-shadow:0 0 0 200vmax #fff;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 1px 0;
  animation: mgm 1s linear infinite alternate;
}

@keyframes mgm {
  to {
    margin: 50% 0;
  }
}
<div class="mgm">
  Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae, blanditiis qui porro possimus commodi laudantium voluptatum accusantium. Maxime alias dolorum quo aliquam delectus qui illo officiis, consequuntur asperiores fugiat ducimus!
</div>

Related