如何使用 Next.Js 实现响应式移动导航菜单?

问题描述 投票:0回答:2

所以,我对如何在 Next.js 中实现响应式移动导航有点困惑。我的问题源于通过 Javascript 和 CSS 处理菜单切换状态。

我的导航菜单遵循 Log Rocket 的 以下教程,但教程中使用的方法是为 React 编写的,没有考虑 Next.js 如何应用 CSS 类名。简单的 React 示例依赖于动态更新 CSS,而大多数 Next.js 方法似乎都建议使用 useState 来切换元素。这可行,但我失去了能够通过媒体查询和切换按钮控制导航的粒度。

我当前的实现检查布尔 useState 值来显示菜单,我只想将其应用于 780px 或更低的菜单。如果我从

false
状态开始,则不会呈现整个菜单,包括切换菜单的按钮。如果我从
true
状态开始,那么移动菜单将呈现为自动展开,这是我想防止的。当用户不在移动设备上时,这也会导致切换完全删除菜单(例如,如果菜单未切换,并且用户使用大屏幕尺寸。

那么,问题就变成了如何防止这种意外行为?或者如何将移动菜单默认状态设置为隐藏,同时保留点击切换功能?

import { useState } from "react";
import Link from "next/Link";
import SiteLogo from "../components/CompLogo";
import navStyles from "../styles/Nav.module.css";

const Nav = () => {
  const [isNavExpanded, setIsNavExpanded] = useState(true);
  return (
    <div>
      <nav className={navStyles.nav}>
        <SiteLogo />
        <button
          className={navStyles.hamburger}
          onClick={() => {
            setIsNavExpanded(!isNavExpanded);
            console.log("clicked");
            console.log(isNavExpanded);
          }}
        >
          {/* icon from heroicons.com */}
          <svg
            xmlns="http://www.w3.org/2000/svg"
            className="h-5 w-5"
            viewBox="0 0 20 20"
            fill="currentColor"
          >
            <path
              fillRule="evenodd"
              d="M3 5a1 1 0 011-1h12a1 1 0 110 
              2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 
              01-1- 1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
              clipRule="evenodd"
            />
          </svg>
        </button>
        <div className={navStyles.navigation}>
          {isNavExpanded && (
            <ul className={navStyles.menu}>
              <li className={navStyles.links}>
                <Link href="/">
                  <a className={navStyles.anchors}>Home</a>
                </Link>
              </li>
              <li className={navStyles.links}>
                <Link href="/map">
                  <a className={navStyles.anchors}>Map Page</a>
                </Link>
              </li>
              <li className={navStyles.links}>
                <Link href="/about">
                  <a className={navStyles.anchors}>About</a>
                </Link>
              </li>
            </ul>
          )}
        </div>
      </nav>
    </div>
  );
};

导出默认导航

这是我的CSS:

.one {
  margin-top: 0;
  height: 100vh;
  width: 100%;
  background-image: url("../../public/images/My\ project.jpg");
  background-position: top;
  background-repeat: no-repeat;
  background-size: cover;
  display: flex;
  justify-content: left;
  align-items: center;
}

.oneAppointment {
  width: 55%;
  margin-left: 10%;
  text-align: left;
  /* display: flex; */
  /* justify-content: center; */
  /* flex-direction: column; */
  /* height: 40%; */
  /* background-color: #fff; */
  /* padding: 20px 40px; */
  /* border-radius: 6px; */
}

.oneAppointment h1 {
  font-size: 40px;
  font-weight: 700;
  /* color: #000; */
}

.abc {
  font-size: 56px;
  font-weight: 700;
  /* color: blanchedalmond; */
}

.heading {
  font-size: 40px;
  font-weight: 700;
  /* background-color: blueviolet; */
  margin-bottom: 10px;
  /* color: #000; */
}
.designer a {
  margin-top: 16px;
}

.designer {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 520px;
  width: 100%;
  background-image: url("../../public/images/fas.jpg");
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  /* background-color: blueviolet; */
}

.blogs {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 520px;
  width: 100%;
  background-image: url("../../public/images/fas.jpg");
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  /* background-color: blueviolet; */
}

.collection {
  margin-top: 0;
  height: 90vh;
  width: 100%;
  /* background-image: url("./website/images/pexels-yan-krukov-4458418.jpg"); */
  background-image: url("../../public/images/collec.jpg");
  background-position: top;
  background-repeat: no-repeat;
  background-size: cover;
  display: flex;
  justify-content: center;
  align-items: center;
}

.collections {
  width: 45%;
  margin-left: 45%;
  text-align: center;
  margin-top: -100px;
}

.shop {
  margin-top: 0;
  height: 90vh;
  width: 100%;
  /* background-image: url("./website/images/pexels-yan-krukov-4458418.jpg"); */
  background-image: url("../../public/images/shopp.jpg");
  background-position: top;
  background-repeat: no-repeat;
  background-size: cover;
  display: flex;
  justify-content: center;
  align-items: center;
}

.shopContent {
  width: 45%;
  margin-left: 45%;
  text-align: center;
  margin-top: -100px;
}

@media only screen and (min-width: 1px) and (max-width: 768px) {
  .heading {
    font-size: 36px;
    font-weight: 700;
    margin-bottom: 10px;
  }

  .one {
    margin-top: 0;
    background-image: url("../../public/images/mmy.jpg");
    background-position: left;
  }

  .oneAppointment {
    width: 30%;
    margin-left: 5%;
    text-align: left;
    margin-top: -80px;
  }

  .oneAppointment h1 {
    font-size: 28px;
    font-weight: 700;
    line-height: 55px;
  }

  .abc {
    font-size: 40px;
    font-weight: 700;
    /* color: blanchedalmond; */
  }

  .designer {
    text-align: center;
    height: 600px;
    width: 90%;
    margin-left: 5%;
    background-position: left;
    background-size: cover;
  }
  .blogs {
    margin-top: 100px;
    text-align: center;
    height: 600px;
    width: 90%;
    margin-left: 5%;
    background-position: left;
    background-size: cover;
  }
  .collection {
    /* margin-top: 0;
    height: 90vh;
    width: 100%; */
    /* background-image: url("./website/images/pexels-yan-krukov-4458418.jpg"); */
    /* background-image: url("../public/images/collec.jpg"); */
    background-image: url("../../public/images/colle.jpg");
    background-position: left;
    /* background-position: calc(-10px); */
    /* background-repeat: no-repeat;
    background-size: cover;
    display: flex; */
    justify-content: left;
    /* align-items: center; */
  }

  .collections {
    width: 70%;
    margin-left: 25%;
    /* background-color: aquamarine; */
    text-align: right;
    margin-top: -140px;
  }

  .shop {
    margin-top: -100px;
    /* margin-top: 0;
    height: 90vh;
    width: 100%; */
    /* background-image: url("./website/images/pexels-yan-krukov-4458418.jpg"); */
    /* background-image: url("../public/images/shopp.jpg"); */
    background-position: calc(-280px);
    /* background-repeat: no-repeat; */
    /* background-size: cover; */
    /* display: flex; */
    justify-content: left;
    /* align-items: center; */
  }
  .shopContent {
    width: 70%;
    margin-left: 25%;
    /* background-color: chartreuse; */
    text-align: right;
    margin-top: 0px;
  }
}
.nav {
  background-color: white;
  color: black;
  width: 100%;
  display: flex;
  align-items: center;
  position: relative;
  padding: 0.5rem 0 rem;
}

.navigation {
  margin-left: auto;
}

.menu {
  display: flex;
  padding: 0;
}

.links {
  list-style-type: none;
  margin: 0 1rem;
}

.links a {
  text-decoration: none;
  display: block;
  width: 100%;
}

.hamburger {
  border: 0;
  height: 80px;
  width: 80px;
  padding: 0.5rem;
  border-radius: 50%;
  background-color: white;
  cursor: pointer;
  transition: background-color 0.2s ease-in-out;
  position: absolute;
  top: 50%;
  right: 5px;
  transform: translateY(-50%);
  display: none;
}

@media screen and (max-width: 768px) {
  .hamburger {
    display: block;
  }
  .menu {
    position: absolute;
    top: 146px;
    left: 0;
    flex-direction: column;
    width: 100%;
    height: calc(100vh - 147px);
    background-color: white;
    border-top: 1px solid black;
  }

  .links {
    text-align: center;
    margin: 0;
  }
  .links a {
    color: black;
    width: 100%;
    padding: 1.5rem 0;
  }
  .links:hover {
    background-color: #eee;
  }
  .menu.expanded {
    display: block;
  }
}
next.js responsive-design media-queries
2个回答
1
投票

不遵循您的代码,而是一般指南 -

*重要 - 这仅供您理解,可能与您的代码不完全相同。

对于您来说,宽度是780px,您要检查和设置移动菜单和桌面菜单。

第 1 步 - 您必须检查屏幕的宽度,您可以使用 CSS 媒体屏幕宽度来执行此操作,但随后您的组件将加载移动菜单和桌面菜单的所有数据,因此您可以编写一个事件监听器宽度改变并做一些事情,(假设宽度是一种状态)-

{width > 780px ? <DesktopMenu /> : <MobileMenu /> }

现在您可以延迟加载这两个组件。

现在来到移动菜单部分 -

  • “打开菜单抽屉按钮”默认情况下应始终在移动屏幕上可见(切换按钮打开)。

  • 在 openMenuDrawerButton 上编写一个点击事件来打开一个实际保存菜单布局的抽屉,并使用 CSS 实现幻灯片效果或其他任何东西。

  • 再次使用状态来检查抽屉是否打开,让我们将此状态称为 visibleDrawer,然后使用 openMenuDrawerButton {visibleDrawer : true} 以及抽屉本身中存在的关闭按钮切换此状态。

     {!visibleDrawer && < OpenMenuDrawerButton /> }
    

单击 opendrawer 按钮(打开抽屉)后隐藏它。

  • 一旦抽屉打开,在抽屉上显示一个关闭按钮,这将隐藏抽屉 {visibleDrawer : false} 并使 OpenMenuDrawerButton 出现在其位置。

如果您需要更多解释,请写信。


1
投票

我使用以下技巧来使用 CSS 媒体查询隐藏/显示适当的元素,该查询在设置任何状态之前运行(因此是“默认”行为)。我使用MaterialUI,希望你能适应你的需求:

{/* Mobile Version */}
<Hidden smUp implementation="css">
...
</Hidden>

{/* Desktop Version */}
<Hidden smDown implementation="css">
...
</Hidden>

您可以在 useState 中应用其他逻辑,以实现默认加载后所需的任何设置。

© www.soinside.com 2019 - 2024. All rights reserved.