6

I'm trying to animate an elements height using css. The way I'm doing it is, I added a touch event to an element. The function adds a className to the element that's supposed to hide, i.e. get a height of 0.

The problem is, when the element gets clicked, the div that's supposed get a height of 0 pauses a second then gets the desired height. It seems like the longer the animation duration is, the long it waits before it animates.

Here's the relevant code:

transition: max-height 2s ease-in-out;

JSFiddle

var heading = document.getElementById('heading'),
  body = document.getElementById('body');

heading.addEventListener('click', hide);
body.addEventListener('transitionend', hideCallback);

function hide() {
  body.className = 'hide';
}

function hideCallback(e) {
  console.log(e.propertyName);
}
#wrapper {
  margin: auto;
  background-color: blue;
  width: 470px;
  text-align: center;
  overflow: hidden;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.17);
  max-height: 400px;
  max-width: 600px;
  padding: 0;
}
#buttonContainer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  align-content: center;
  flex-direction: column;
  margin: auto;
  width: 100px;
  height: 100px;
  color: white;
}
#buttonIcon {
  width: 36px;
  height: 36px;
  line-height: 36px;
  font-size: 35px;
  font-weight: bold;
  display: inline-block;
}
#buttonLabel {
  font-size: 20px;
}
#content {
  width: 100%;
  height: 100%;
  text-align: left;
  transform: translateY(0px);
}
#heading {
  color: white;
  text-align: right;
  margin: 10px;
  font-size: 17px;
}
#body {
  color: #000;
  color: rgba(0, 0, 0, 0.87);
  background-color: #FFF;
  width: 100%;
  max-height: 500px;
  padding: 10px 0;
  box-sizing: border-box;
}
#body.hide {
  transition: max-height 2s ease-in-out;
  max-height: 0;
}
#innerBody {
  height: 200px;
  background-color: orange;
}
<div id="wrapper">
  <div id="buttonContainer"><span id="buttonIcon">My</span><span id="buttonLabel">self</span>
  </div>
  <div id="content">
    <div id="heading">Press Me</div>
    <div id="body">
      <div id="innerBody"></div>
    </div>
  </div>
</div>
Jessica
  • 9,379
  • 14
  • 65
  • 136
  • Replied also [here](http://stackoverflow.com/questions/18087098/how-to-remove-delay-on-css3-slide-out-transition-which-uses-max-height-transitio?answertab=votes#tab-top) – Lionel T Apr 18 '16 at 22:20

3 Answers3

2

Your initial max-height is set to 500px but the actual height is much less. In the transition, that 500px is gradually decreased towards 0. At first that has no visual effect, until the max-height becomes smaller then the actual height. this produces the "delay" you are seeing.

You should either transition the actual height property to 0 (but that only works if the initial height is set as well, to 200px in your case). Or you could look into transitioning a transform: scaleY(0), which has the added benefit of being much cheaper and giving a much smoother animation. (http://www.html5rocks.com/en/tutorials/speed/high-performance-animations/)

Pevara
  • 14,242
  • 1
  • 34
  • 47
  • Thanks for the answer! The reason I can't use `scaleY(200)` is for two reasons. 1 - That scales things down to the middle, not to the top or bottom. 2 - It doesn't remove the space. – Jessica Apr 18 '16 at 22:55
  • 1
    You can set a `transform-origin`, but that won't change the fact that the space remains. That is also the reason why it is so much cheaper btw. Anyway, fixed heights it is then. @OriDrori answer probably has the code you are looking for... – Pevara Apr 18 '16 at 23:00
2

You're animating max-height and not height. Animating max-height is a trick that's used for animating the height of elements with an unknown height. You state a very large max-height, and as long as the unknown height is less than the max-height, it works. The only downside is that since your animating something that is bigger than your actual height, it will have a delay. In this case your max-height is 500px, and the height (including padding) is only 220px, which means that more than have the time (~1.1sec) is just reducing the max-height to 220px, and when it gets there, the visual starts to animate.

If, like this example, you know the actual height of the element (220 = padding 10 + innerBody height 200), you can animate the exact height.

If you don't know the height beforehand, try to lower max-height estimates and use an easing that starts fast, like ease-out or use javascript to set the start and end height.

Known height: 220px (watch in full screen):

var heading = document.getElementById('heading'),
  body = document.getElementById('body');

heading.addEventListener('click', hide);
body.addEventListener('transitionend', hideCallback);

function hide() {
  body.className = 'hide';
}

function hideCallback(e) {
  console.log(e.propertyName);
}
#wrapper {
  margin: auto;
  background-color: blue;
  width: 470px;
  text-align: center;
  overflow: hidden;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.17);
  max-height: 400px;
  max-width: 600px;
  padding: 0;
}
#buttonContainer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  align-content: center;
  flex-direction: column;
  margin: auto;
  width: 100px;
  height: 100px;
  color: white;
}
#buttonIcon {
  width: 36px;
  height: 36px;
  line-height: 36px;
  font-size: 35px;
  font-weight: bold;
  display: inline-block;
}
#buttonLabel {
  font-size: 20px;
}
#content {
  width: 100%;
  height: 100%;
  text-align: left;
  transform: translateY(0px);
}
#heading {
  color: white;
  text-align: right;
  margin: 10px;
  font-size: 17px;
}
#body {
  color: #000;
  color: rgba(0, 0, 0, 0.87);
  background-color: #FFF;
  width: 100%;
  height: 220px;
  padding: 10px 0;
  box-sizing: border-box;
}
#body.hide {
  transition: height 2s ease-in-out;
  height: 0;
}
#innerBody {
  height: 200px;
  background-color: orange;
}
<div id="wrapper">
  <div id="buttonContainer"><span id="buttonIcon">My</span><span id="buttonLabel">self</span>
  </div>
  <div id="content">
    <div id="heading">Press Me</div>
    <div id="body">
      <div id="innerBody"></div>
    </div>
  </div>
</div>

Unknown height with javascript - set the actual height before the start of the animation, wait for next frame, and change it to 0 (watch in full screen):

var heading = document.getElementById('heading'),
  body = document.getElementById('body');

heading.addEventListener('click', hide);
body.addEventListener('transitionend', hideCallback);

function hide() {
  body.style.height = body.scrollHeight + 'px'; /** sample actual height, and set it on element **/
  requestAnimationFrame(function() { // wait for next frame
    body.style.height = 0; // change height to 0
  });
}

function hideCallback(e) {
  console.log(e.propertyName);
}
#wrapper {
  margin: auto;
  background-color: blue;
  width: 470px;
  text-align: center;
  overflow: hidden;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.17);
  max-height: 400px;
  max-width: 600px;
  padding: 0;
}
#buttonContainer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  align-content: center;
  flex-direction: column;
  margin: auto;
  width: 100px;
  height: 100px;
  color: white;
}
#buttonIcon {
  width: 36px;
  height: 36px;
  line-height: 36px;
  font-size: 35px;
  font-weight: bold;
  display: inline-block;
}
#buttonLabel {
  font-size: 20px;
}
#content {
  width: 100%;
  height: 100%;
  text-align: left;
  transform: translateY(0px);
}
#heading {
  color: white;
  text-align: right;
  margin: 10px;
  font-size: 17px;
}
#body {
  color: #000;
  color: rgba(0, 0, 0, 0.87);
  background-color: #FFF;
  width: 100%;
  padding: 10px 0;
  box-sizing: border-box;
  transition: height 2s ease-in-out;
}

#innerBody {
  height: 200px;
  background-color: orange;
}
<div id="wrapper">
  <div id="buttonContainer"><span id="buttonIcon">My</span><span id="buttonLabel">self</span>
  </div>
  <div id="content">
    <div id="heading">Press Me</div>
    <div id="body">
      <div id="innerBody"></div>
    </div>
  </div>
</div>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
0

try some other CSS transition-timing-function like linear or ease-out.

As per w3schools,

ease-in-out Specifies a transition effect with a slow start and end (equivalent to cubic-bezier(0.42,0,0.58,1))

Bhabishya Kumar
  • 731
  • 3
  • 7