front/react

height:0; overflow:hidden; -> height:auto; transition animation 적용이 안 될 때 해결 방법 - react-animate-height

jeong_ga 2022. 7. 4. 16:51

Navigation 부분 작성 중 발견한 문제로 css에서는 높이값이 지정되지 않았을 때 (height:auto) transition을 적용시킬 수 없다.

하지만 나는 navigation 부분은 list의 개수가 다르고, max-height:100vw 와 같은 코드를 적용시킬 수 없기 때문에 높이를 지정할 수 없었음!

이를 해결하기 위해서는 js로 style을 적용시켜야 하지만!! react는 이미 라이브러리가 만들어져 있다 😉

react-animate-height - how to use

[react-animate-height

Lightweight React component for animating height using CSS transitions.. Latest version: 3.0.4, last published: 15 days ago. Start using react-animate-height in your project by running `npm i react-animate-height`. There are 329 other projects in the npm r

www.npmjs.com](https://www.npmjs.com/package/react-animate-height)

npm에 나와있는 예시 코드대로 작성하니 무난하게 성공했음!

코드 핵심

import { useState } from "react";
import AnimateHeight from "react-animate-height";

import NavPartList from "./NavPartList";

const NavInnerButton = styled.button`
    // 기타 style code

    // arrow
    & > div:first-child:after,
    & > div:first-child::after {
      display: block;
      content: "";
      position: absolute;
      right: 0;
      top: 10px;
      width: 7px;
      height: 7px;
      border-top: 1px solid ${({ theme }) => theme.colors.pointGray};
      border-left: 1px solid ${({ theme }) => theme.colors.pointGray};
      transform: rotate(135deg);
      transition: all 0.3s ease-in;
    }
  }

  .sub_list_show {
    width: 100%;
    // arrow
    & > div:first-child:after,
    & > div:first-child::after {
      transform: rotate(225deg);
    }
  }
`;

function NavInnerSub({ item }) {
  const [height, setHeight] = useState(0);
  const [more, setMore] = useState(false);
  const onClickMore = () => {
    setMore(!more);
    setHeight(height === 0 ? "auto" : 0);
  };

  return (
    <NavInnerButton
      aria-expanded={height !== 0}
      aria-controls="example-panel"
      onClick={onClickMore}
    >
      <div className={more ? "sub_list sub_list_show" : "sub_list"}>
        <NavPartList item={item} />
        <AnimateHeight id="example-panel" duration={300} height={height}>
          <ul>
              <li></li>
          </ul>
        </AnimateHeight>
      </div>
    </NavInnerButton>
  );
}
export default NavInnerSub;

적용 완료.gif


코드 전문보기

import { useState } from "react";
import { Link as RRDLink, useLocation } from "react-router-dom";
import styled from "styled-components";
import AnimateHeight from "react-animate-height";

import NavPartList from "./NavPartList";

const Link = ({ children, isActive, ...props }) => {
  return <RRDLink {...props}>{children}</RRDLink>;
};
const NavInnerButton = styled.button`
  width: 100%;
  height: auto;
  padding: 0;
  padding-right: 25px;
  margin-top: 25px;
  margin-left: 30px;
  text-decoration: none;
  border: none;
  background-color: transparent;
  text-align: left;
  cursor: pointer;

  .sub_list {
    ul {
      width: 180px;
      margin-left: 30px;
      padding-bottom: 5px;
    }
    li {
      width: 100%;
      height: auto;
      line-height: 41px;
    }
    // NavInner Name
    & > div {
      position: relative;
    }
    // arrow
    & > div:first-child:after,
    & > div:first-child::after {
      display: block;
      content: "";
      position: absolute;
      right: 0;
      top: 10px;
      width: 7px;
      height: 7px;
      border-top: 1px solid ${({ theme }) => theme.colors.pointGray};
      border-left: 1px solid ${({ theme }) => theme.colors.pointGray};
      transform: rotate(135deg);
      transition: all 0.3s ease-in;
    }
  }

  .sub_list_show {
    width: 100%;
    // arrow
    & > div:first-child:after,
    & > div:first-child::after {
      transform: rotate(225deg);
    }
  }
`;

const StyledSubLink = styled(Link)`
  text-decoration: none;
  font-family: ${({ theme }) => theme.font.point};
  font-size: 14px;
  color: ${(props) => (props.isActive ? "#64C5B1" : "#101038")};
`;

function NavInnerSub({ item }) {
  const { pathname } = useLocation();
  const [height, setHeight] = useState(0);
  const [more, setMore] = useState(false);
  const onClickMore = () => {
    setMore(!more);
    setHeight(height === 0 ? "auto" : 0);
  };

  return (
    <NavInnerButton
      aria-expanded={height !== 0}
      aria-controls="example-panel"
      onClick={onClickMore}
    >
      <div className={more ? "sub_list sub_list_show" : "sub_list"}>
        <NavPartList item={item} />
        <AnimateHeight id="example-panel" duration={300} height={height}>
          <ul>
            {item.subNav.map((item, key) => (
              <li key={key}>
                <StyledSubLink to={item.url} isActive={pathname === item.url}>
                  {item.subName}
                </StyledSubLink>
              </li>
            ))}
          </ul>
        </AnimateHeight>
      </div>
    </NavInnerButton>
  );
}
export default NavInnerSub;