您的第一个使用 React 和 React-Spring 的响应式动画导航栏

2025-06-10

您的第一个使用 React 和 React-Spring 的响应式动画导航栏

在本教程中,你将使用React JS构建一个简单、响应式且动画化的导航栏。你将使用styled-components编写 CSS,并使用react-springs实现动画。

您可以在此处查看该项目的最终结果。

Styled Components 是一个 CSS-in-JS 样式框架,它使用 JavaScript 中的带标签模板字面量和 CSS 的强大功能,提供了一个平台,允许你编写真实的 CSS 来设置 React 组件的样式。本质上,Styled Components 是使用 styled-components 库编写的易于编写的 React 组件,你可以在 JavaScript 代码中使用纯 CSS 来设置组件的样式。React-Spring 是一个基于 Spring Physics 的动画库,它能够以非常简单的方式满足你大部分 UI 相关的动画需求。

开始吧!

我假设您的计算机上已经安装了节点,您可以通过在终端中输入以下内容来检查它:

node -v
Enter fullscreen mode Exit fullscreen mode

如果没有,请安装最新版本

好的,一切就绪!您将使用create-react-app启动一个新项目,因此请打开终端并输入:

npx create-react-app react-navbar
Enter fullscreen mode Exit fullscreen mode

并添加项目期间所需的依赖项

npm install styled-components react-spring
Enter fullscreen mode Exit fullscreen mode

现在使用您最喜欢的代码编辑器打开新创建的项目,转到src文件夹并删除app.cssindex.csslogo.svg

打开index.js文件并删除以下行:

import ./index.css
Enter fullscreen mode Exit fullscreen mode

同时打开你的app.js并清理它,使其看起来像这样:

import React from react;

const App = () =>  {
  return(
    <div>

    </div>
  )
}

export default App;
Enter fullscreen mode Exit fullscreen mode

在您的src文件夹中创建一个名为style的目录,并在其中包含Global.js文件,同时在您的src文件夹中创建一个components文件夹,一个名为navbar的子文件夹,并在该文件夹中创建以下文件:Brand.js , BurgerMenu.js , CollapseMenu.js 和 Navbar.js

我们还将在src文件夹中创建一个名为assets的文件夹,并在其中放置一张图片,您可以从这里下载该文件。

您的项目树应该如下所示:

项目树

太好了,这将是你的起点。

首先,您要使用 Styled Components 创建全局样式文件,打开Global.js文件并添加以下代码:

import { createGlobalStyle } from 'styled-components';

const GlobalStyles = createGlobalStyle`

  @import url('https://fonts.googleapis.com/css?family=Montserrat:400,600&display=swap');;

  *,
  *::after,
  *::before {
    margin: 0px;
    padding: 0px;
    box-sizing: inherit;
  }

  html {
    font-size: 62.5%;
  }

  body {
    box-sizing: border-box;
    font-family: 'Montserrat', sans-serif;
  }
`;

export default GlobalStyles;
Enter fullscreen mode Exit fullscreen mode

现在您将打开您的App.js文件,将其重构为类组件,添加我们需要的依赖项,添加Global.jsNavbar.js组件以及导航栏的状态。

import React, { Component } from 'react'
import Navbar from "./components/navbar/Navbar";

import GlobalStyle from './styles/Global';

class App extends Component {
  state = {
    navbarOpen: false
  }

  handleNavbar = () => {
    this.setState({ navbarOpen: !this.state.navbarOpen });
  }

  render() {

    return (
      <>
        <Navbar 
          navbarState={this.state.navbarOpen} 
          handleNavbar={this.handleNavbar}
        />
        <GlobalStyle />
      </>
    )
  }
}

export default App
Enter fullscreen mode Exit fullscreen mode

太好了,在开始处理Navbar.js组件之前,您将处理小组件,打开您的Brand.js并使其看起来像这样:

import React from 'react'
import styled from "styled-components";

import logo from "../../assets/logo-min.png";

const Brand = () => {
  return (
    <Image src={logo} alt="Company Logo" />
  )
}

export default Brand

const Image = styled.img`
  height: 85%;
  margin: auto 0;
`;
Enter fullscreen mode Exit fullscreen mode

现在BurgerMenu.js

import React from 'react';
import styled from "styled-components";

const Burgermenu = (props) => {
  return (
    <Wrapper onClick={props.handleNavbar}>
      <div className={ props.navbarState ? "open" : "" }>
        <span>&nbsp;</span>
        <span>&nbsp;</span>
        <span>&nbsp;</span>
      </div>
    </Wrapper>
  );
}

export default Burgermenu;

const Wrapper = styled.div`
  position: relative;
  padding-top: .7rem;
  cursor: pointer;
  display: block;

  & span {
    background: #fdcb6e;
    display: block;
    position: relative;
    width: 3.5rem;
    height: .4rem;
    margin-bottom: .7rem;
    transition: all ease-in-out 0.2s;
  }

  .open span:nth-child(2) {
      opacity: 0;
    }

  .open span:nth-child(3) {
    transform: rotate(45deg);
    top: -11px;
  }

  .open span:nth-child(1) {
    transform: rotate(-45deg);
    top: 11px;
  }

`;
Enter fullscreen mode Exit fullscreen mode

太好了,现在是最后一个组件,首先是CollapseMenu.js

import React from 'react';
import styled from 'styled-components';

import { useSpring, animated } from 'react-spring';

const CollapseMenu = (props) => {
  const { open } = useSpring({ open: props.navbarState ? 0 : 1 });

  if (props.navbarState === true) {
    return (
      <CollapseWrapper style={{
        transform: open.interpolate({
          range: [0, 0.2, 0.3, 1],
          output: [0, -20, 0, -200],
        }).interpolate(openValue => `translate3d(0, ${openValue}px, 0`),
      }}
      >
        <NavLinks>
          <li><a href="/" onClick={props.handleNavbar}>link n1</a></li>
          <li><a href="/" onClick={props.handleNavbar}>link n2</a></li>
          <li><a href="/" onClick={props.handleNavbar}>link n3</a></li>
          <li><a href="/" onClick={props.handleNavbar}>link n4</a></li>
        </NavLinks>
      </CollapseWrapper>
    );
  }
  return null;
};

export default CollapseMenu;

const CollapseWrapper = styled(animated.div)`
  background: #2d3436;
  position: fixed;
  top: 4.5rem;
  left: 0;
  right: 0;
`;

const NavLinks = styled.ul`
  list-style-type: none;
  padding: 2rem 1rem 2rem 2rem;

  & li {
    transition: all 300ms linear 0s;
  }

  & a {
    font-size: 1.4rem;
    line-height: 2;
    color: #dfe6e9;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;

    &:hover {
      color: #fdcb6e;
      border-bottom: 1px solid #fdcb6e;
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

最后是Navbar.js

import React from 'react'
import styled from "styled-components";
import { useSpring, animated, config } from "react-spring";

import Brand from "./Brand";
import BurgerMenu from "./BurgerMenu";
import CollapseMenu from "./CollapseMenu";

const Navbar = (props) => {
  const barAnimation = useSpring({
    from: { transform: 'translate3d(0, -10rem, 0)' },
    transform: 'translate3d(0, 0, 0)',
  });

  const linkAnimation = useSpring({
    from: { transform: 'translate3d(0, 30px, 0)', opacity: 0 },
    to: { transform: 'translate3d(0, 0, 0)', opacity: 1 },
    delay: 800,
    config: config.wobbly,
  });

  return (
    <>
      <NavBar style={barAnimation}>
        <FlexContainer>
          <Brand />
          <NavLinks style={linkAnimation}>
            <a href="/">link n1</a>
            <a href="/">link n2</a>
            <a href="/">link n3</a>
            <a href="/">link n4</a>
          </NavLinks>
          <BurgerWrapper>
            <BurgerMenu
              navbarState={props.navbarState} 
              handleNavbar={props.handleNavbar}
            />
          </BurgerWrapper>
        </FlexContainer>
      </NavBar>
      <CollapseMenu 
        navbarState={props.navbarState} 
        handleNavbar={props.handleNavbar}
      />
   </>
  )
}

export default Navbar

const NavBar = styled(animated.nav)`
  position: fixed;
  width: 100%;
  top: 0;
  left: 0;
  background: #2d3436;
  z-index: 1;
  font-size: 1.4rem;
`;

const FlexContainer = styled.div`
  max-width: 120rem;
  display: flex;
  margin: auto;
  padding: 0 2rem;;
  justify-content: space-between;
  height: 5rem;
`;

const NavLinks = styled(animated.ul)`
  justify-self: end;
  list-style-type: none;
  margin: auto 0;

  & a {
    color: #dfe6e9;
    text-transform: uppercase;
    font-weight: 600;
    border-bottom: 1px solid transparent;
    margin: 0 1.5rem;
    transition: all 300ms linear 0s;
    text-decoration: none;
    cursor: pointer;

    &:hover {
      color: #fdcb6e;
      border-bottom: 1px solid #fdcb6e;
    }

    @media (max-width: 768px) {
      display: none;
    }
  }
`;

const BurgerWrapper = styled.div`
  margin: auto 0;

  @media (min-width: 769px) {
    display: none;
  }
`;
Enter fullscreen mode Exit fullscreen mode

您已完成!

如果您不想编写所有这些代码,您可以fork我为此示例创建的github repo 。

很快会看到更多提示和教程!

链接:https://dev.to/nunocpnp/your-very-first-responsive-and-animated-navigation-bar-with-react-and-react-spring-17co
PREV
将 dev.to 帖子嵌入到您自己的网站
NEXT
使用无服务器函数保护 API 密钥