맘에 쏙 드는 네비게이션바 만들기..(1)
모든 움직임을 framer-motion으로 해결할 필요는 없었다...
이 블로그를 반응형으로 만들고 싶은데 브레이크포인트마다 뚝뚝 끊기는 게 싫어서 일단 헤더의 navbar부터 수정을 시작했다.
지금 하는 사이드 프로젝트에서 framer를 사용해 디자인 시스템을 작업하고, 아마도 애니메이션은 framer-motion을 사용할 것 같아서 헤더에도 framer-motion을 적용해볼까 했다.
화면 크기에 따라 다른 애니메이션을 적용해야 한다는 생각에 구글에 framer-motion media query를 검색하고 useMediaQuery라는 훅을 베껴왔다.(이 블로그에서..)
import { useEffect, useState } from 'react';
const useMediaQuery = ({ query }: { query: string }) => {
const [matches, setMatches] = useState(false);
useEffect(() => {
const media = window.matchMedia(query);
if (media.matches !== matches) {
setMatches(media.matches);
}
const listener = () => {
setMatches(media.matches);
};
if (typeof media.addEventListener === 'function') {
media.addEventListener('change', listener);
} else {
media.addListener(listener);
}
return () => {
if (typeof media.removeEventListener === 'function') {
media.removeEventListener('change', listener);
} else {
media.removeListener(listener);
}
};
}, [matches, query]);
return matches;
};
export default useMediaQuery;
그리고 이런 훅도 만들었다.
import useMediaQuery from './useMediaQuery';
const useIsMobile = () => useMediaQuery({ query: '(max-width: 640px)' });
export default useIsMobile;
화면 상단 네비게이션 바의 왼쪽, 내 닉네임이 있는 부분(이하, 홈버튼)가 640px를 기준으로 그것보다 작으면 닉네임 왼쪽 공간이 좁고, 크면 왼쪽 공간도 넓기를 바랐다. 그래서 대략 이런 식으로 코드를 작성했다.
const isMobile = useIsMobile();
...
return (
<motion.header
Layout
layoutRoot
initial={false}
animate={isMobile ? "mobile" : "desktop"}
className="sticky top-0 left-0 w-full bg-white opacity-80"
>
<nav className="w-full grid grid-cols-3 items-center relative h-16 px-2">
<motion.div
variants={{ mobile: { x: 0 }, desktop: { x: 5 } }}
transition={ ease: "easeOut", duration: 0.3 }
>
<Link
href="/"
className="font-extralight text-xl"
>
hyongti
</Link>
</motion.nav>
</motion.header>
);
...
그런데... 새로고침을 할 때마다 홈버튼이 움직였다. 모바일이면 모바일 위치에서, 데스크톱이면 데스크톱 위치에서 안 움직이길 바랐는데, useMediaQuery 훅이 컴포넌트가 마운트 된 후에 useEffect 내부에서 window.matchMedia를 통해 현재 스크린 사이즈를 비교해 state 값을 바꿔주기 때문이었다..
모바일 사이즈든 데스크톱 사이즈든 새로고침 했을 때에는 애니메이션이 없이 홈버튼이 원하는 위치에 있었음 했는데, useMediaQuery는 그냥 베껴왔고, framer-motion은 처음 써보고.. 그래서 내가 애니메이션 설정을 잘못해줬나.. 생각하면서 css로만 해결할 수 있는 걸 오래 삽질했다..
...
return (
<header className="sticky top-0 left-0 w-full bg-white opacity-80">
<nav className="w-full grid grid-cols-3 items-center relative h-16 px-2">
<Link
href="/"
className="font-extralight text-xl translate-x-0 transition-transform sm:translate-x-6 duration-300"
>
hyongti
</Link>
...
</nav>
</header>
);
...