Acla Acla - 2 months ago 12
CSS Question

Center element with animation / transition

I'm trying to create a full screen menu that does a bottom-to-top movement and I'm having trouble when it comes to vertically centering it.

Basically, it comes out of the screen and should end up right in the middle of it (centered).

However, since it is a fixed menu with an unknown height and I'm using animations, the options available aren't many:


  • I can't use the
    margin: auto
    technique because the
    auto
    value doesn't work with transitions;

  • I'm trying to avoid using flexbox;

  • translateY()
    seems to work fine but it creates a top-to-bottom movement instead of the desired bottom-to-top one (see my code)

  • anything else? (preferably that works with older browsers, but I can also manage with using
    translateY
    if there's a way to change the direction)





$('#small-nav-btn').click(function() {
$('#overlay').addClass('open');
$('#close-menu-cross').addClass('open');
$('#nav').addClass('open');
})

$('#cross').click(function() {
$('#overlay').removeClass('open');
$('#close-menu-cross').removeClass('open');
$('#nav').removeClass('open');
})

* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: "Now-Regular", sans-serif;
font-size: 12px;
font-weight: bold;
}
ul {
list-style-type: none;
}
a {
color: black;
}
#overlay {
background: #fff;
opacity: 0;
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 0;
transition: all 1s ease 0s;
z-index: 1555;
}
#overlay.open {
opacity: 1;
height: 100%;
}
#small-nav-bar {
display: block;
width: 80%;
margin: 0 auto;
text-align: center;
color: black;
}
#small-nav-btn {
cursor: pointer;
}
#nav {
background: orange;
position: fixed;
top: -100%; /*I need it to be bottom: -100% for the bottom-top movement*/
left: 50%;
transform: translate(-50%, -50%);
transition: all 0.8s linear 0.1s;
z-index: 1556;
}
#nav.open {
top: 50%; /*Again, I need this to be bottom: 50%*/
}
#close-menu-cross.open {
display: block;
position: fixed;
top: 15px;
right: 20px;
z-index: 1556;
cursor: pointer;
}
#close-menu-cross {
display: none;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav id="nav-container">
<div id="small-nav-bar">
<div id="small-nav-btn">BUTTON</div>
</div>

<ul id="nav">
<li><a href="contact.html"><span>HELLO</span></a>
</li>
<li><a href="about.html"><span>HELLO</span></a>
</li>
</ul>

<div id="close-menu-cross">
<div id="cross">X</div>
</div>
</nav>





jsfiddle

Thanks in advance! :)

Answer

You were quite close. With just a few adjustments in the CSS, you have a full working demo:

$('#small-nav-btn').click(function() {
  $('#overlay').addClass('open');
  $('#close-menu-cross').addClass('open');
  $('#nav').addClass('open');
})

$('#cross').click(function() {
  $('#overlay').removeClass('open');
  $('#close-menu-cross').removeClass('open');
  $('#nav').removeClass('open');
})
#nav {
  background: orange;
  position: fixed;
  top: 100%;                           /* 1 */
  left: 50%;
  transform: translate(-50%, 0);       /* 2 */
  transition: all 0.8s linear 0.1s;
  z-index: 1556;
}
#nav.open {
  top: 50%;
  transform: translate(-50%, -50%);    /* 2 */
}

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  font-family: "Now-Regular", sans-serif;
  font-size: 12px;
  font-weight: bold;
}
ul {
  list-style-type: none;
}
a {
  color: black;
}
#overlay {
  background: #fff;
  opacity: 0;
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 0;
  transition: all 1s ease 0s;
  z-index: 1555;
}
#overlay.open {
  opacity: 1;
  height: 100%;
}
#small-nav-bar {
  display: block;
  width: 80%;
  margin: 0 auto;
  text-align: center;
  color: black;
}
#small-nav-btn {
  cursor: pointer;
}
#close-menu-cross.open {
  display: block;
  position: fixed;
  top: 15px;
  right: 20px;
  z-index: 1556;
  cursor: pointer;
}
#close-menu-cross {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav id="nav-container">
  <div id="small-nav-bar">
    <div id="small-nav-btn">BUTTON</div></div>
  <ul id="nav">
    <li><a href="contact.html"><span>HELLO</span></a></li>
    <li><a href="about.html"><span>HELLO</span></a></li>
  </ul>
  <div id="close-menu-cross">
    <div id="cross">X</div>
  </div>
</nav>

Notes:

  1. The CSS offset properties (top, bottom, left, right), when applied to absolutely-positioned elements (which includes position: fixed), shift the element x-distance from the named edge.

    You have top: -100% in your code. This means the element is positioned 100% above the top edge.

    You then have it shifting to top: 50%. This means the element will be shifted halfway inside the container.

    Essentially, your animation moves the element a distance of 150%, from above the window to inside it. The movement is top to bottom.

    But you want the movement to go from bottom to top.

    So push the element all the way to the bottom and off-screen (top: 100%), and have it shift up to halfway inside the container (top: 50%).

  2. The transform: translate() rule simply fine-tunes the centering.

    If you keep translateY(-50%) on the main rule, it will shift 50% of the nav onto the screen before the transition (demo).

    That's why I only used translateY(-50%) for the transitioned element.

    For a complete explanation see my answer here: Element will not stay centered, especially when re-sizing screen

jsFiddle