5단계로 Tailwind로 메뉴를 여는 애니메이션을 만드는 방법

헤이 개발자! 🌼

저는 개발자 학생이고 다른 사람들을 도울 수 있는 콘텐츠 제작을 시작하고 싶습니다. 저는 개발자 커뮤니티에서 시작하고 있기 때문에 초보자들이 겪는 많은 어려움에 공감할 수 있으며, 그 중 일부는 너무 단순해 보이고 인터넷에 내용 설명조차 없다는 것을 알고 있습니다.

오늘은 표지 gif에 있는 것과 같은 메뉴를 만드는 것을 도와드리겠습니다. 보너스로 현재 페이지를 탐색 모음에서 다른 색상으로 설정했습니다. 이 기사에서는 React 애플리케이션을 사용할 것입니다. 즉, 다른 종류의 프레임워크를 사용하는 경우(또는 전혀 사용하지 않는 경우) 일부 프로세스가 다를 수 있습니다. 페이지 스타일을 변경하는 논리를 만들기 위해 React Hooks를 사용하겠습니다.

프로젝트는 여기에서 사용할 수 있습니다repository. 이를 사용하려면 README 파일의 지침을 따르기만 하면 됩니다. 또한 보고 싶다면 결과와 함께 deploy이 있습니다.

나는 내 프로젝트에서 이 기능을 사용하는 것을 좋아하지만 방법을 설명하는 튜토리얼을 본 적이 없습니다. 그럼 시작하겠습니다!😄

1. 프로젝트에 Tailwind 설치

따라서 먼저 Tailwind를 설치해야 합니다. documentation(코딩할 때 가장 친한 친구라고도 함)를 살펴보는 것이 좋습니다.

2. 컴포넌트 생성

이제 메뉴 구성 요소를 만들어야 합니다. 나는 React를 사용할 때 그것을 분리하여 페이지에 추가하는 것을 선호합니다. 다음은 타이머 프로젝트에서 생성한 탐색 메뉴의 예입니다.

// component:  Nav
import  logo  from  '../assets/images/icon.png'
import  Button  from  '../components/Button';
import  LinkComponent  from  "./LinkComponent";

const  Nav  = () => {
    return (
        <nav>
            <Button
                func={() =>  setIsOpen(!isOpen)}
                txt={<img  alt="menu-burguer"  src={logo} />}
            />
            <div>
                <LinkComponent  path={"/"} txt={"Home Page"} />
                <LinkComponent  path={"/countdown"} txt={"Countdown"} />
                <LinkComponent  path={"/timer"} txt={"Timer"} />
                <LinkComponent  path={"/settings"} txt={"Settings"} />
                <LinkComponent  path={"/about"} txt={"About"} />
            </div>
        </nav>
    );
}

export  default  Nav;

// component: Button
const  Button  = ({ func, txt, isDisabled, className }) => {
    return (
        <button
            className={className}
            disabled={isDisabled}
            type="button"
            onClick={  func  }
        >
            {  txt  }
        </button>
    );
}

Button.defaultProps  = {
    isDisabled:  false,
} 

export  default  Button;

// component: LinkComponent
import { Link } from  "react-router-dom"

const  LinkComponent  = ({ path, txt }) => {
    return (
        <Link
            to={path}
        >
            {txt}
        </Link>
    );
}

export  default  LinkComponent;


3. 논리 만들기
이 애니메이션이 예상대로 작동하려면 HTML 클래스를 변경할 수 있는 코드가 있어야 합니다. 그것은 메뉴를 열고 닫을 때 다른 애니메이션이 필요하기 때문에 중요합니다. 게다가 바에 설정된 위치는 애니메이션이 끝났을 때 머물러야 할 위치라는 것이 기본입니다.

// component:  Nav
import { useState } from  "react";
import  logo  from  '../assets/images/icon.png'
import  Button  from  '../components/Button';
import  LinkComponent  from  "./LinkComponent";

const  Nav  = () => {
// the state used to change the current situation (open or closed)
    const [isOpen, setIsOpen] =  useState(false);

    return (
        <nav>
            <Button
// when the "menu" button is clicked, it sets the state for the opposite boolean value
                func={() =>  setIsOpen(!isOpen)}
                txt={<img  alt="menu-burguer"  src={logo} />}
            />
// this is the div where the menu is "hidden", so it's where the change of classes needs to happen
            <div
                className={isOpen  ? ('class for open menu') : ('class for closed menu')}
            >
                <LinkComponent  path={"/"} txt={"Home Page"} />
                <LinkComponent  path={"/countdown"} txt={"Countdown"} />
                <LinkComponent  path={"/timer"} txt={"Timer"} />
                <LinkComponent  path={"/settings"} txt={"Settings"} />
                <LinkComponent  path={"/about"} txt={"About"} />
            </div>
        </nav>
    );
}

export  default  Nav;


현재 페이지에 대한 링크가 강조 표시되는 보너스 부분은 더 많은 논리가 필요하기 때문에 조금 더 복잡합니다.

// component: LinkComponent
import { useContext, useEffect, useState } from  "react";
import { Link, useRouteMatch } from  "react-router-dom"
import  TimeContext  from  "../context/TimeContext";

const  LinkComponent  = ({ path, txt }) => {
// first, we need to get the current pathname
    const  pathname  =  useRouteMatch();
    const [isCurrent, setIsCurent] =  useState(false);
    const [currentPath, setCurrentPath] =  useState('/');

// always when the pathname is changed the function occurs
    useEffect(() => {
        setCurrentPath(pathname.path)
    }, [pathname]);

// always when the pathname or the path (props) is changed the function occurs
    useEffect(() => {
        const  changeIsCurrent  = () => {
            if (currentPath  ===  path) {
                setIsCurent(true);
            } else {
                setIsCurent(false);
            }
        }
        changeIsCurrent();
    }, [currentPath, path]);  

    return (
// where happens the highlight depends if it's true (happen) or false (don't happen)
        <Link
            className={isCurrent  ? ('class when the page is the current'): ('class when the page is not the current')}
            to={path}
        >
            {txt}
        </Link>
    );
}

export  default  LinkComponent;


4. 애니메이션 만들기

tailwind.config.js 문서에는 사용자 지정 설정을 추가할 수 있는 개체가 있습니다. 예제와 같이 애니메이션 구성을 추가할 것입니다.

// tailwind.config.js
/** @type  {import('tailwindcss').Config} */

module.exports  = {
    content: ["./src/**/*.{js,jsx,ts,tsx}"],
    theme: {
        extend: {
            animation: {
                openmenu:  'openmenu 1s ease-in',
                closemenu:  'closemenu 1s ease-in',
            },
            keyframes: {
                openmenu: {
                // initial position
                    '0%': {left:  '-224px'},
                // final position
                    '100%': {left:  '0px'}
                },
                closemenu: {
                // initial position
                    '0%': {left:  '0px'},
                // final position
                    '100%': {left:  '-224px'}
                },
            }
        },
    },
    plugins: [],
}


5. 메뉴 구성 요소에 클래스 추가

이제 애니메이션이 있으므로 구성 요소에 추가할 차례입니다. 이것은 내 타이머 프로젝트의 결과이지만 원하는 방식으로 스타일을 지정할 수 있습니다.
경고: 애니메이션의 final position를 구성 요소의 default로 설정하는 것을 잊지 마십시오. 애니메이션이 끝나면 설정된 위치로 리디렉션되기 때문에 필요합니다.

// component:  Nav
import { useState } from  "react";
import  logo  from  '../assets/images/icon.png'
import  Button  from  '../components/Button';
import  LinkComponent  from  "./LinkComponent";

const  Nav  = () => {
    const [isOpen, setIsOpen] =  useState(false);

    return (
        <nav  className="font-epilogue">
            <Button
                className="w-20"
                func={() =>  setIsOpen(!isOpen)}
                txt={<img  alt="menu-burguer"  src={logo} />}
            />
            <div  
                className={isOpen  ?  
                "left-[0px] font-epilogue flex top-[12vh] animate-openmenu w-56 absolute flex-col   bg-light-ocean p-12 h-[88vh]" :  
                "animate-closemenu top-[12vh] left-[-224px] flex w-56 absolute flex-col bg-light-ocean p-12 h-[88vh]"}
            >
                <LinkComponent  path={"/"} txt={"Home Page"} />
                <LinkComponent  path={"/countdown"} txt={"Countdown"} />
                <LinkComponent  path={"/timer"} txt={"Timer"} />
                <LinkComponent  path={"/settings"} txt={"Settings"} />
                <LinkComponent  path={"/about"} txt={"About"} />
            </div>
        </nav>
    );
}

export  default  Nav;

// component: LinkComponent
import { useContext, useEffect, useState } from  "react";
import { Link, useRouteMatch } from  "react-router-dom"
import  TimeContext  from  "../context/TimeContext";

const  LinkComponent  = ({ path, txt }) => {
    const  pathname  =  useRouteMatch();
    const { currentPath } =  useContext(TimeContext);
    const [isCurrent, setIsCurent] =  useState(false);
    const [currentPath, setCurrentPath] =  useState('/');

    useEffect(() => {
        setCurrentPath(pathname.path)
    }, [pathname]);

    useEffect(() => {
        const  changeIsCurrent  = () => {
            if (currentPath  ===  path) {
                setIsCurent(true);
            } else {
                setIsCurent(false);
            }
        }
        changeIsCurrent();
    }, [currentPath, path]);  

    return (
        <Link
            className={isCurrent  ? (
            "mb-3 text-dark-purple font-bold bg-soft-purple p-2 text-center rounded-2xl"): (
            "mb-3 text-dark-purple hover:font-bold p-2 text-center")}
            to={path}
        >
            {txt}
        </Link>
    );
}

export  default  LinkComponent;


이 튜토리얼이 즐거웠기를 바랍니다. 개선할 수 있는 부분이 있으면 주저하지 말고 저에게 연락해 주세요! 모든 피드백을 환영합니다.✨

좋은 웹페이지 즐겨찾기