3

In some interface, I want to do animations. Since the transform property is far more optimized for that than other CSS properties, I'll use that.

With transform: scale() : When element aspect ratio doesn't change, no problem. When ratio is supposed to change, the solution I found is to put a container, and in this container an inner. Then apply the opposed transform to the inner block so the ratio look preserved during and after animation.

I did a pen to test the idea, and it works, but with a problem during the animation: the inner block look stretched during animation. I don't get why given that animation time is the same, and easing is linear.

https://codepen.io/AdamElio/pen/PabejP

(Cick on the menu to toggle animation)

document.querySelector('#menu').addEventListener('click', function() {
    this.classList.toggle('collapsedd')
});
#menu {
  margin: 30px;
  padding: 15px;
  background: white;
  width: 150px;
  overflow: hidden;
  transform-origin: top center;
  transition: transform .5s linear;
}

#menu.collapsedd {
  transform: scaleY(.2);
}

#menu .inner {
  transition: transform .5s linear;
  transform-origin: top center;
}

#menu.collapsedd .inner {
  transform: scaleY(5);
}

#menu .inner ul {
  padding: 0;
  margin: 0;
  list-style: none;
}
<nav id="menu">
  <div class="inner">
    <h5>Menu</h5>
    <ul>
      <li>Link 1</li>
      <li>Link 2</li>
      <li>Link 3</li>
      <li>Link 4</li>
    </ul>
  </div>
</nav>

<div id="transform"></div>
krlzlx
  • 5,752
  • 14
  • 47
  • 55
Adam S
  • 1,021
  • 1
  • 14
  • 25

2 Answers2

3

This happens because the counter transform is not the same while the transition is happening. Considering your example, the container will go from scaleY(0.2) to scaleY(1), while the child will go from scaleY(5) to scaleY(1).

At the end and at the beginning of the transition this will work, cause the container and child transforms will "cancel out":

  • beginning: 0.2 * 5 = 1
  • end: 1 * 1 = 1

But this is not the case for the intermediate steps of the transition. Consider for example when the transition is 50% complete:

  • container scaleY: 0.2 + (1 - 0.2) * 0.5 = 0.6
  • child scaleY: 5 + (1 - 5) * 0.5 = 3
  • container scaleY * child scaleY: 0.6 * 3 = 1.8

Check the graph below to see how this proportion varies:

Proportion variation

The purple line shows the child's scaleY during the transition, the blue line shows the container's scaleY and the dark yellow line shows container scaleY * child scaleY

So, in order to really cancel out the container transform during the transition, you need to use an easing function that holds the expression container scale * child scale = 1 for the whole transition. And that's not an easy task.

Thiago Barcala
  • 6,463
  • 2
  • 20
  • 23
  • 1
    Crystal clear and detailed explanation. So, do you have any suggestion to achieve this in a simpler way ? – Adam S Apr 01 '19 at 09:49
0

I think it's because of this line:

#menu.collapsedd .inner {
  transform: scaleY(5);
}

When collapsedd is added you are scaling the Y of the content to 5, trying changing that value to 1 or just not doing a scale on the inner div. You could use opacity or something to fade it out?

Sam
  • 76
  • 4
  • This menu is a simple example. I'm not trying to specifically solve this, but more being able to inverse transform to preserve ratio in general. I scale the inner 5 time because the container is scaled to 1/5. – Adam S Jun 08 '18 at 13:59
  • I see, well I'm not 100% sure what the overall goal is but commenting out the the transform on the inner div has everything scaling together, but at the end of the animation you just have squished looking text. `/* #menu.collapsedd .inner { transform: scaleY(5); } */` – Sam Jun 08 '18 at 19:39