Akanksha keshari
Akanksha keshari

Reputation: 11

Hamburger menu navigation items animations

https://drive.google.com/file/d/1LCS6Z-oP1rqPbRF7tydgVWZEdCEt_RPP/view?usp=sharing this the animation effect on hamburger menu navigation menu items. On click on the hamburger menu there is animation going on each navigation menu items.
I have tried to recreate one. https://codepen.io/coral_Sb/pen/VwmQbLo I don't understand where I getting wrong?

The animation is playing everytime when hamburger menu is clicked.

.nav-menu {
  background-color: red;
}

.nav-menu ul li a {
  color: #fff;
  text-decoration: none;
  margin-bottom: 20px;
}

li:first-child {
  animation: bringback 1s 0s forwards;
}

li:nth-child(2) {
  animation: bringback 1s 2s forwards;
}

.nav-menu li:nth-child(3) {
  animation: bringback 1s 3s forwards;
}

.nav-menu li:nth-child(4) {
  animation: bringback 1s 4s forwards;
}

.nav-menu li:nth-child(5) {
  animation: bringback 1s 5s forwards;
}

.nav-menu li:nth-child(6) {
  animation: bringback 1s 6s forwards;
}

.nav-menu li:nth-child(7) {
  animation: bringback 1s 7s forwards;
}

.nav-menu li:nth-child(8) {
  animation: bringback 1s 8s forwards;
}

.nav-menu li:nth-child(9) {
  animation: bringback 1s 9s forwards;
}

.nav-menu li:nth-child(10) {
  animation: bringback 1s 10s forwards;
}

@keyframes bringback {
  to {
    opacity: 1;
    text-indent: 25px;
  }
}
<div class="nav-menu" id="nav-menu">

  <ul id=" check-ul" style="list-style:none">
    <li>
      <!-- class="active1"-->
      <span><img src="images/birla-logo.png" alt=""></span>
    </li>
    <br>
    <li>
      <a href="#">Home</a>
    </li>
    <li>
      <a href="#">About us <span class="fas fa-sort-down mr-3" style="width: 20px;"></span> </a>
    </li>
    <li>
      <a href="#">Curriculum</a>
    </li>
    <li>
      <a href="#">Criteria</a>
    </li>
    <li>
      <a href="#">Admission Process</a>
    </li>
    <li>
      <a href="#">Careers </a>
    </li>
    <li>
      <a href="#">Contact us </a>
    </li>
    <li>
      <a href="#">Enquire Now </a>
    </li>
  </ul>
</div>

Upvotes: 1

Views: 1100

Answers (2)

Roko C. Buljan
Roko C. Buljan

Reputation: 206068

Don't hardcode CSS for an arbitrary number of items li:nth-child(N) - it's hard to maintain.

Sequentially staggered delay animation

Use CSS variables to create a staggered animation by using fill-mode both and control the delay using that CSS variable inside CSS calc():

#check-ul li {
  animation: animate 350ms ease calc(var(--i) * 200ms) both;
}

@keyframes animate {
  0% {
    opacity: 0;
    transform: translateX(-2em);
  }
  
  100% {
    opacity: 1;
  }
}
<div class="nav-menu" id="nav-menu">
  <ul id="check-ul">
    <li style="--i:1"><a href="#">Home</a></li>
    <li style="--i:2"><a href="#">About us</a></li>
    <li style="--i:3"><a href="#">Curriculum</a></li>
    <li style="--i:4"><a href="#">Criteria</a></li>
    <li style="--i:5"><a href="#">Admission Process</a></li>
    <li style="--i:6"><a href="#">Careers </a></li>
    <li style="--i:7"><a href="#">Contact us</a></li>
    <li style="--i:8"><a href="#">Enquire Now</a></li>
  </ul>
</div>

Trigger menu animations on open via button click:

const toggleTarget = (sel) => {
  const EL_targets = document.querySelectorAll(sel);
  EL_targets.forEach(EL => EL.classList.toggle("is-active"));
};

const EL_toggleButtons = document.querySelectorAll("[data-toggle]");
EL_toggleButtons.forEach(EL => EL.addEventListener("click", (ev) => {
  toggleTarget(ev.currentTarget.dataset.toggle);
}));
#nav-menu {
  position: fixed;
  background: #d00;
  color: #fff;
  top: 0;
  left: 0;
  height: 100vh;
  padding: 20px;
  transition: 0.3s;
  transform: translateX(-100%);
}

#nav-menu.is-active {
  transform: translateX(0%);
}

#nav-menu.is-active li {
  animation: animate 350ms ease calc(var(--i) * 100ms) both;
}

@keyframes animate {
  0% {
    opacity: 0;
    transform: translateX(-2em);
  }
  100% {
    opacity: 1;
  }
}
<button type="button" data-toggle="#nav-menu">OPEN MENU</button>
<div id="nav-menu">
  <button type="button" data-toggle="#nav-menu">CLOSE</button>
  <ul>
    <li style="--i:1"><a href="#">Home</a></li>
    <li style="--i:2"><a href="#">About us</a></li>
    <li style="--i:3"><a href="#">Curriculum</a></li>
    <li style="--i:4"><a href="#">Criteria</a></li>
    <li style="--i:5"><a href="#">Admission Process</a></li>
    <li style="--i:6"><a href="#">Careers </a></li>
    <li style="--i:7"><a href="#">Contact us</a></li>
    <li style="--i:8"><a href="#">Enquire Now</a></li>
  </ul>
</div>

Upvotes: 2

dev-cyprium
dev-cyprium

Reputation: 768

In order to complete a full example, I think you need some JavaScript in the mix.

Let's approach this problem by first creating the necessary components to properly display the navigation from the video:

<div class="navigation">
  <button id="toggle-button">
  <svg class="menu-button" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
  </svg>
</button>

And style it accordingly:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  font-family: 'Roboto';
}

#toggle-button {
  background: none;
    color: inherit;
    border: none;
    padding: 0;
    font: inherit;
    cursor: pointer;
    outline: inherit;
}

.navigation {
  background: #1f2937;
}

.menu-button {
  color: white;
  width: 2.3rem;
  height 2.3rem;
}

What we need to do next is prepare the necessary markup for the panel, fader, and side menu.

<div class="overlay-container hidden">
    <div class="overlay"></div>
    <div class="panel">
      <ul class="menu">
        <li class="hidden" style="--i:1">Menu item #1</li>
        <li class="hidden" style="--i:2">Menu item #2</li>
        <li class="hidden" style="--i:3">Menu item #3</li>
        <li class="hidden" style="--i:4">Menu item #4</li>
      </ul>
    </div>
  </div>

And of course style it:

.overlay-container.hidden {
  display: none;
}

.overlay-container {
  overflow: hidden;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

.overlay.fade {
  opacity: 0.6;
}

.overlay {
  position: absolute;
  width: 100vw;
  height: 100vh;
  opacity: 0;
  background: black;
  transition: opacity 300ms ease-out;
}

.panel.slide {
  transform: translateX(0%);
}

.panel {
  position: absolute;
  height: 100vh;
  width: 18rem;
  background: #c00136;
  transform: translateX(-100%);
  transition: transform 300ms ease-out;
}

.menu {
  padding: 1rem 1rem;
  list-style-type: none;
}

.menu li.hidden {
  visibility: hidden;
}

.menu li {
  color: #f7f7f7;
  margin: 1rem 0;
}

.menu li.fadeInLeft {
  animation: fadeInLeft 350ms ease calc(var(--i) * 200ms) both;
}

@keyframes fadeInLeft {
  0% {
    opacity: 0;
    transform: translateX(-2em);
  }

  100% {
    opacity: 1;
  }
}

Now using this bit of javascript, you can orchestrate everything together. Notice how I used some transitions as well as animations so we can sequentially time

let button = document.getElementById("toggle-button");
let backbox = document.querySelectorAll('.overlay-container')[0];
let overlay = document.querySelectorAll('.overlay')[0];
let panel = document.querySelectorAll('.panel')[0];

/*
 * Since transitions from 'hidden'
 * to 'block' don't animate we need 
 * to trigger a browser repaint by
 * asking the element for it's height
 */
function forceRepaint(element) {
  return element.offsetHeight;
}

button.addEventListener("click", function () {
  backbox.classList.remove('hidden');
  forceRepaint(backbox);
  overlay.classList.add('fade');
  panel.classList.add('slide');
});

panel.addEventListener("transitionend", function () {
  document.querySelectorAll('.menu li').forEach(node => {
    node.classList.remove("hidden");
    node.classList.add('fadeInLeft');
  })
});

I suggest you approach these problems gradually and step by step, that way you'll be able to solve these problems yourself in the future too. Problems like this seem complex at first, but it's essential that you break them down into smaller parts.

Finally, here's how it all comes together.

let button = document.getElementById("toggle-button");
let backbox = document.querySelectorAll('.overlay-container')[0];
let overlay = document.querySelectorAll('.overlay')[0];
let panel = document.querySelectorAll('.panel')[0];

/*
 * Since transitions from 'hidden'
 * to 'block' don't animate we need 
 * to trigger a browser repaint by
 * asking the element for it's height
 */
function forceRepaint(element) {
  return element.offsetHeight;
}

button.addEventListener("click", function() {
  backbox.classList.remove('hidden');
  forceRepaint(backbox);
  overlay.classList.add('fade');
  panel.classList.add('slide');
});

panel.addEventListener("transitionend", function() {
  document.querySelectorAll('.menu li').forEach(node => {
    node.classList.remove("hidden");
    node.classList.add('fadeInLeft');
  })
});
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  font-family: 'Roboto';
}

#toggle-button {
  background: none;
  color: inherit;
  border: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
  outline: inherit;
}

.navigation {
  background: #1f2937;
}

.menu-button {
  color: white;
  width: 2.3rem;
  height 2.3rem;
}

.overlay-container.hidden {
  display: none;
}

.overlay-container {
  overflow: hidden;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

.overlay.fade {
  opacity: 0.6;
}

.overlay {
  position: absolute;
  width: 100vw;
  height: 100vh;
  opacity: 0;
  background: black;
  transition: opacity 300ms ease-out;
}

.panel.slide {
  transform: translateX(0%);
}

.panel {
  position: absolute;
  height: 100vh;
  width: 18rem;
  background: #c00136;
  transform: translateX(-100%);
  transition: transform 300ms ease-out;
}

.menu {
  padding: 1rem 1rem;
  list-style-type: none;
}

.menu li.hidden {
  visibility: hidden;
}

.menu li {
  color: #f7f7f7;
  margin: 1rem 0;
}

.menu li.fadeInLeft {
  animation: fadeInLeft 350ms ease calc(var(--i) * 200ms) both;
}

@keyframes fadeInLeft {
  0% {
    opacity: 0;
    transform: translateX(-2em);
  }
  100% {
    opacity: 1;
  }
}
<div class="navigation">
  <button id="toggle-button">
  <svg class="menu-button" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
  </svg>
  </button>

  <div class="overlay-container hidden">
    <div class="overlay"></div>
    <div class="panel">
      <ul class="menu">
        <li class="hidden" style="--i:1">Menu item #1</li>
        <li class="hidden" style="--i:2">Menu item #2</li>
        <li class="hidden" style="--i:3">Menu item #3</li>
        <li class="hidden" style="--i:4">Menu item #4</li>
      </ul>
    </div>
  </div>
</div>

EDIT I used the snippet from Roko C. Buljan's answer to change my timing logic, as I was doing that with javascript and it makes the example so much simpler.

Upvotes: 0

Related Questions