Ru

Animated dropdown menu in React using – onAnimationEnd

12.08.2023

animated dropdown menu in react

In this component, when creating animation, onAnimationEnd is used. onAnimationEnd is a handler for the animationEnd event, which is called when the CSS animation of an element ends. This component does not use third-party libraries for animation and when the menu is closed, it is not in the DOM.

 

App.jsx

import Menu from "./components/menu";
function App() {
  return (
    <div className="App">
      <Menu/>
    </div>
   );
}
export default App;
 

index.jsx (menu)

import styles from "./menu.module.css";
import { useState, useEffect, useRef } from "react";
import Kitten from "../SVG/Kitten";
export default function Menu() {
  const [showMenu, setShowMenu] = useState(false);
  const [fadeOut, setFadeOut] = useState(false);
  const buttonShowMenu = (Visibility) => {
    if (Visibility) {
      setShowMenu(true);
    } else {
      setFadeOut(true);
    }
  };

  // Click tracking outside the menu
  const menuRef = useRef();
  useEffect(() => {
    if (menuRef.current) {
      const handler = (e) => {
        if (!menuRef.current.contains(e.target)) {
          setFadeOut(true);
        }
      };
      document.addEventListener("mousedown", handler);
      return () => {
        document.removeEventListener("mousedown", handler);
      };
    }
  });
  return (
    <div className={styles.menu_wrap}>
      <button
        onClick={() => buttonShowMenu(!showMenu)}
      >
        <Kitten />
      </button>
      {showMenu && (
        <div
          ref={menuRef}
          className={
            fadeOut
              ? `${styles.menu} ${styles.menu__fade_out}`
              : `${styles.menu}`
          }
          onAnimationEnd={(e) => {
            if (e.animationName === styles.fadeOut) {
              setShowMenu(false);
              setFadeOut(false);
            }
          }}
        >
          <ul className={styles.menu_list}>
            <li>My profile</li>
            <li>Settings</li>
            <li>Exit</li>
          </ul>
        </div>
      )}
    </div>
  );
}
 

menu.module.css

.menu_wrap {
  width: 100%;
  max-width: 200px;
  margin-right: 15px;
  margin-left: 15px;
}

button {
  margin-left: auto;
  margin-right: auto;
  margin-top: 7px;
  margin-bottom: 7px;
  width: 50px;
  height: 50px;
  display: flex;
  justify-content: center;
  align-items: center;
  color: #222;
  box-shadow: rgb(0 0 0 / 18%) 0.2px 0.2px 3px;
  border-radius: 50%;
  background-color: rgb(255, 255, 255);
  transition: all 250ms ease;
  cursor: pointer;
  border: none;
}

@media (min-width: 1000px) {
  button:hover {
    transition: all 250ms ease;
    box-shadow: rgb(0 0 0 / 18%) 0.3px 0.3px 5px;
  }
}

.menu {
  width: 100%;
  max-width: 100px;
  left: 0;
  right: 0;
  margin: auto;
  border-radius: 4px;
  position: absolute;
  animation: fadeIn 0.3s forwards;
  opacity: 0;
  background-color: rgb(255, 255, 255);
  box-shadow: rgb(0 0 0 / 18%) 0.2px 0.2px 4px;
}

.menu_list li {
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 17px;
  color: #222;
  cursor: pointer;
  transition: all 300ms ease;
  height: 25px;
  margin-top: 12px;
  margin-bottom: 12px;
}

@media (min-width: 1000px) {
  .menu_list li:hover {
    transition: all 300ms ease;
    color: #545454;
  }
}

.menu__fade_out {
  animation: fadeOut 0.3s forwards;
  opacity: 1;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes fadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

Look at GitHub, Live

Share

Copy

BTC (Network BTC) - 1C2EWWeEXVhg93hJA9KovpkSd3Rn3BkcYm

Ethereum (Network ERC20) - 0x05037ecbd8bcd15631d780c95c3799861182e6b8

This website uses cookies. By clicking the 'Accept' button or continuing to use the website, you agree to the use of cookies.