Как создать выезжающее меню на React с анимацией

    В этой статье мы разберём реализацию мобильного выезжающего меню (off-canvas menu) на React с использованием хуков useState, useEffect и useContext. Вы узнаете, как управлять состоянием бургер-кнопки, анимировать появление панели и блокировать прокрутку основного контента. Готовый код подходит для адаптивных веб-приложений и интернет-магазинов.

    Компонент Menu на React

    Основной компонент Menu использует локальное состояние isExpanded для отслеживания открытия/закрытия панели. При изменении состояния через useEffect добавляется или удаляется класс menu-open на document.body, что позволяет управлять CSS-анимациями и блокировать скролл. Контекст AuthContext определяет, показывать ссылки для авторизованного пользователя или гостя.

    function Menu() {
      const [isExpanded, setIsExpanded] = useState(false);
      const { userId } = useContext(AuthContext);
    
      useEffect(() => {
        if (isExpanded) {
          document.body.classList.add("menu-open");
        } else {
          document.body.classList.remove("menu-open");
        }
        return () => {
          document.body.classList.remove("menu-open");
        };
      }, [isExpanded]);
    
      return (
        <>
          <button className="burger-button" onClick={() => setIsExpanded(!isExpanded)}>
            ☰
          </button>
          <aside className="menu">
            <div className="menu-header">
              <div className="logo" onClick={() => setIsExpanded(!isExpanded)}>
                <Logo />
                <Sidekick />
              </div>
            </div>
            <nav className={
    `mobile-nav ${isExpanded ? "expanded" : ""}`
            }>
              {userId ? (
                <>
                  <a>Profile info</a>
                  <a>Statistics</a>
                </>
              ) : (
                <>
                  <a>Sing In</a>
                  <a>Sing Up</a>
                </>
              )}
            </nav>
          </aside>
        </>
      );
    }

    CSS-стили для выезжающей панели

    Стили реализуют плавное выезжание меню справа с использованием transform: translateX(100%) и opacity: 0. При открытии через класс body.menu-open панель сдвигается в видимую область. Бургер-кнопка скрыта на десктопе и появляется только при ширине экрана до 500px. Для дополнительной адаптации под маленькие экраны (до 376px) уменьшаются размеры кнопки.

    .burger-button {
      display: none;
      position: absolute;
      top: 0;
      right: 0;
      height: 48px;
      width: 48px;
      background-color: transparent;
      border: none;
      font-size: 24px;
      z-index: 101;
      color: var(--text-color);
    }
    
    .menu {
      display: block;
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      width: 80vw;
      height: 100vh;
      background-color: var(--background-root);
      border-left: 1px solid var(--border-color);
      transform: translateX(100%);
      opacity: 0;
      transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
      z-index: 100;
    }
    
    .mobile-nav {
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 10px;
      gap: 10px;
      width: 80vw;
      height: calc(100vh - 48px);
      top: 48px;
      right: 0;
      z-index: 100;
      background-color: var(--background-root);
    }
    
    @media (max-width: 500px) {
      .burger-button {
        display: flex;
        justify-content: center;
        align-items: center;
      }
    
      body.menu-open .menu {
        transform: translateX(0);
        opacity: 1;
      }
    
      body.menu-open .menu-header {
        opacity: 1;
        transform: translateX(0);
        display: flex;
      }
    
      body.menu-open main,
      body.menu-open footer {
        filter: blur(4px);
        pointer-events: none;
      }
    }

    Особенности реализации

    При открытии меню на main и footer накладывается размытие (blur) и отключаются события мыши. Это стандартный UX-паттерн для мобильных навигаций. Логотип в шапке меню также служит кнопкой закрытия. Обратите внимание, что в коде используется useEffect с очисткой - это предотвращает утечку класса menu-open при размонтировании компонента.

    Адаптация под разные экраны

    Для экранов шире 500px бургер-кнопка скрыта, а меню остаётся за пределами видимой области. При ширине до 376px размер кнопки уменьшается до 32x32 пикселей, что удобно для устройств с маленьким дисплеем. Вы можете легко изменить 80vw на фиксированную ширину (например, 300px) для более предсказуемого поведения.

    Часто задаваемые вопросы