Frame motion
리액트용 모션 라이브러리이다.
https://www.framer.com/motion/
Documentation | Framer for Developers
An open source, production-ready motion library for React on the web.
www.framer.com
Frame motion 설치
우선 framer-motion을 설치한다.
npm i framer-motion
라이브러리를 가져온다.
import { motion } from "framer-motion"
애니메이션 효과를 주고 싶은 태그의 앞에 motion 키워드를 붙여서 motion 컴포넌트를 만들어 준다.
<motion.div></motion.div>
만약 Frame motion을 사용하여 스타일 컴포넌트 만들고 싶다면 확장된 스타일 컴포넌트를 사용하면 된다.
const Box = styled(motion.div)``;
Frame motion의 Basic Animation
애니메이션 구성을 하는 방법은 여러가지가 있다.
기본적인 방법은 모션 컴포넌트에 직접 모든 props를 담는 것이다.
가장 기초적인 props값에는 아래와 같은 것들이 있다.
initial - 컴포넌트의 초기 상태를 지정한다.
animate - 원하는 애니메이션 효과를 지정한다.
transition - transition 타입을 지정한다.
<Box initial={{ scale: 0 }} animate={{ scale: 1, rotateZ: 360 }} />
Frame motion의 Variants
variants는 애니메이션 세트라고 할 수 있다.
단일 객체가 아닌 DOM 전체에 전파되는 애니메이션을 만들 경우 variants를 사용하면 좋다.
const boxVariants = {
start: {
opacity: 0,
scale: 0.5,
},
end: {
scale: 1,
opacity: 1,
transition: {
type: "spring",
duration: 0.5,
bounce: 0.5,
delayChildren: 0.5, // 자식 요소의 딜레이 설정
staggerChildren: 0.2, // 자식 요소에 개별적 딜레이 설정
},
},
};
const circleVariants = {
start: {
opacity: 0,
y: 10,
},
end: {
opacity: 1,
y: 0,
},
};
<Box variants={boxVariants} initial="start" animate="end">
<Circle variants={circleVariants} />
<Circle variants={circleVariants} />
<Circle variants={circleVariants} />
<Circle variants={circleVariants} />
</Box>
자식 요소는 부모 요소의 variants 객체의 키를 그대로 상속 받는다.
따라서 자식 요소에 새로운 variants를 주기만 하면 알아서 적용된다.
부모 요소에서 자식 요소의 애니메이션을 제어하게 할 수도 있다.
https://www.framer.com/motion/transition/#orchestration
SVG animation

fontawesome 사이트에서 원하는 아이콘의 SVG code를 복사한다.
SVG 코드는 아래와 같은 구조일 것이다.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path d="~~~~~" />
</svg>
1. svg 스타일 컴포넌트를 생성한다.
const Svg = styled.svg`
width: 300px;
height: 300px;
path {
stroke: white;
stroke-width: 2;
}
`;
2. path를 모션 컴포넌트화 시킨다.
<motion.path
variants={svg}
initial="start"
animate="end"
d="~~~"
></motion.path>
3. path 컴포넌트 varaints를 생성한다.
const svg = {
start: { pathLength: 0, fill: "rgba(255, 255, 255, 0)" },
end: {
fill: "rgba(255, 255, 255, 1)",
pathLength: 1,
transition: {
default: { duration: 5 },
fill: { duration: 1, delay: 3 }, //fill 변화 시간을 다르게 줄 수 있다.
},
},
};
AnimatePresence
AnimatePresence는 framer에서 제공하는 컴포넌트이다.
특정 컴포넌트가 마운트되거나(나타나거나) 언마운트 될 때(사라질 때)의 애니메이션을 처리할 수 있다.
기본적인 동작은 아래와 같다.
1. AnimatePresence 컴포넌트를 생성한다.
이때 컴포넌트 내부에는 showing state에 따라 마운트, 언마운트 될 수 있도록 조건문(함수)를 둔다.
<AnimatePresence> </AnimatePresence>
2. 조건문 속에 하위 컴포넌트를 둔다.
<AnimatePresence>
{showing ? <Box/> : null}
</AnimatePresence>
3. 하위 컴포넌트의 variants를 생성하고 할당한다.
const boxVariants = {
initial: {
opacity: 0,
scale: 0,
},
visible: {
opacity: 1,
scale: 1,
rotateZ: 360,
},
leaving: {
opacity: 0,
scale: 0,
y: 50,
},
};
<AnimatePresence>
{showing ? (
<Box
variants={boxVariants}
initial="initial"
animate="visible"
exit="leaving"
/>
) : null}
</AnimatePresence>
AnimatePresence가 컴포넌트의 마운트 상태에 따라 initial, animate, exit를 각각 실행시킨다.
AnimatePresence - 슬라이더 만들기
Animate Presence를 이용하여 슬라이더를 만들 것이다.
0. 기본적인 슬라이더 구조를 만든다.
1. visible state를 생성한다.
2. 각 버튼에 setVisible을 사용한 함수를 이벤트 리스너로 단다.
3. Box의 key를 visible로 둔다.
컴포넌트는 고유의 key가 있기 때문에 key가 바뀔 경우 새로운 컴포넌트로 인식하고 리렌더링 된다.
4. variants를 생성하여 할당한다.
<Wrapper>
<AnimatePresence>
<Box
variants={boxVariants}
initial="invisible"
animate="visible"
exit="exit"
key={visible}
>
{visible}
</Box>
</AnimatePresence>
<button onClick={onNext}>next</button>
<button onClick={onPrev}>prev</button>
</Wrapper>
const boxVariants = {
invisible: {
x: 800,
opacity: 0,
scale: 0,
},
visible: {
x: 0,
opacity: 1,
scale: 1,
transition: {
duration: 2,
},
},
exit: { x: -500, opacity: 0, scale: 0, transition: { duration: 1 } },
};
이 variants는 단방향의 애니메이션만이 작동하도록 되어있다.
반대방향의 애니메이션도 동작하도록 해보자.
우리가 원하는 대로 variants를 조작할 수 있게 AnimatePresence 컴포넌트와 하위 컴포넌트에 custom props를 보낸다.
custom은 variants에 데이터를 보낼 수 있게 하는 props이다.
custom을 쓰게 되면 variants는 객체를 리턴하는 함수가 되어야 한다.
<AnimatePresence mode="wait" custom={back}>
<Box
custom={back}
variants={boxVariants}
initial="invisible"
animate="visible"
exit="exit"
key={visible}
>
{visible}
</Box>
</AnimatePresence>;
const boxVariants = {
invisible: (back: boolean) => ({
x: back ? -500 : 500,
opacity: 0,
scale: 0,
}),
visible: {
x: 0,
opacity: 1,
scale: 1,
transition: {
duration: 2,
},
},
exit: (back: boolean) => ({
x: back ? 500 : -500,
opacity: 0,
scale: 0,
transition: { duration: 1 },
}),
};
layout animation
layout animation : 레이아웃에 따른 컴포넌트 애니메이션 처리
모션 컴포넌트의 layout이라는 prop는 레이아웃이 바뀔 때마다 자동으로 애니메이션을 적용한다.
<Wrapper onClick={toggleClicked}>
<Box
style={{
justifyContent: clicked ? "center" : "flex-start",
alignItems: clicked ? "center" : "flex-start",
}}
>
<Circle layout />
</Box>
</Wrapper>;
나의 경우에는 layout prop이 제대로 작동하지 않아서 하나의 컴포넌트에서 layout 애니메이션을 처리할 때도 layoutId를 사용하였다. 작동되지 않는 이유는 찾지 못했다
shared layout animation
동일한 layoutId prop을 가졌다면 다른 컴포넌트라도 연결시켜 애니메이션을 적용시킬 수 있다.
layoutId가 있는 새 컴포넌트가 추가되고 다른 컴포넌트가 제거되면 이전 컴포넌트에서 새 컴포넌트로 레이아웃 애니메이션을 수행한다. 이는 시각적으로 하나의 연속 컴포넌트로 처리된다.
<Wrapper onClick={toggleClicked}>
<Box>
{!clicked ? (
<Circle layoutId="circle" style={{ borderRadius: 50 }} />
) : null}
</Box>
<Box>
{clicked ? (
<Circle layoutId="circle" style={{ borderRadius: 0, scale: 2 }} />
) : null}
</Box>
</Wrapper>;
layout animation - 간단한 보드 애니메이션 만들기

layout 애니메이션을 사용하면 위와 같은 예시의 보드를 간단하게 만들 수 있다.
id state가 null인지 아닌지에 따라 오버레이 컴포넌트를 마운트 시킬 수 있다.
오버레이 컴포넌트 하위에는 layoutId를 공유하는 Box 컴포넌트를 두어 애니메이션을 적용할 수 있게 한다.
function App() {
const [id, setId] = useState<null | string>(null);
return (
<Wrapper>
<Grid>
{["1", "2", "3", "4"].map((i) => (
<Box onClick={() => setId(i)} key={i} layoutId={i}></Box>
))}
</Grid>
<AnimatePresence>
{id ? (
<Overlay
onClick={() => setId(null)}
variants={overlay}
initial="hidden"
animate="visible"
exit="exit"
>
<Box layoutId={id} style={{ width: 400, height: 200 }} />
</Overlay>
) : null}
</AnimatePresence>
</Wrapper>
);
}
const overlay = {
hidden: { backgroundColor: "rgba(0, 0, 0, 0)" },
visible: { backgroundColor: "rgba(0, 0, 0, 0.5)" },
exit: { backgroundColor: "rgba(0, 0, 0, 0)" },
};
레이아웃 애니메이션의 다양한 사용 예시는 아래 링크에서 확인할 수 있다.
https://www.framer.com/docs/animate-shared-layout/#syncing-layout-animations
AnimateSharedLayout | Framer for Developers
Animate layout changes across, and between, multiple components.
www.framer.com
'Tech > TypeScript' 카테고리의 다른 글
[ TS 기초 ] 타입스크립트란 (0) | 2023.03.21 |
---|---|
[Typescript / TS] 영화 웹 서비스 만들기 (0) | 2023.02.07 |
[Typescript / TS] ToDo 칸반보드 만들기 (0) | 2023.01.23 |
[Typescript / TS] ToDo리스트 만들기 (0) | 2023.01.16 |
[Typescript / TS] Recoil을 사용한 state 관리 (0) | 2023.01.16 |
Frame motion
리액트용 모션 라이브러리이다.
https://www.framer.com/motion/
Documentation | Framer for Developers
An open source, production-ready motion library for React on the web.
www.framer.com
Frame motion 설치
우선 framer-motion을 설치한다.
npm i framer-motion
라이브러리를 가져온다.
import { motion } from "framer-motion"
애니메이션 효과를 주고 싶은 태그의 앞에 motion 키워드를 붙여서 motion 컴포넌트를 만들어 준다.
<motion.div></motion.div>
만약 Frame motion을 사용하여 스타일 컴포넌트 만들고 싶다면 확장된 스타일 컴포넌트를 사용하면 된다.
const Box = styled(motion.div)``;
Frame motion의 Basic Animation
애니메이션 구성을 하는 방법은 여러가지가 있다.
기본적인 방법은 모션 컴포넌트에 직접 모든 props를 담는 것이다.
가장 기초적인 props값에는 아래와 같은 것들이 있다.
initial - 컴포넌트의 초기 상태를 지정한다.
animate - 원하는 애니메이션 효과를 지정한다.
transition - transition 타입을 지정한다.
<Box initial={{ scale: 0 }} animate={{ scale: 1, rotateZ: 360 }} />
Frame motion의 Variants
variants는 애니메이션 세트라고 할 수 있다.
단일 객체가 아닌 DOM 전체에 전파되는 애니메이션을 만들 경우 variants를 사용하면 좋다.
const boxVariants = {
start: {
opacity: 0,
scale: 0.5,
},
end: {
scale: 1,
opacity: 1,
transition: {
type: "spring",
duration: 0.5,
bounce: 0.5,
delayChildren: 0.5, // 자식 요소의 딜레이 설정
staggerChildren: 0.2, // 자식 요소에 개별적 딜레이 설정
},
},
};
const circleVariants = {
start: {
opacity: 0,
y: 10,
},
end: {
opacity: 1,
y: 0,
},
};
<Box variants={boxVariants} initial="start" animate="end">
<Circle variants={circleVariants} />
<Circle variants={circleVariants} />
<Circle variants={circleVariants} />
<Circle variants={circleVariants} />
</Box>
자식 요소는 부모 요소의 variants 객체의 키를 그대로 상속 받는다.
따라서 자식 요소에 새로운 variants를 주기만 하면 알아서 적용된다.
부모 요소에서 자식 요소의 애니메이션을 제어하게 할 수도 있다.
https://www.framer.com/motion/transition/#orchestration
SVG animation

fontawesome 사이트에서 원하는 아이콘의 SVG code를 복사한다.
SVG 코드는 아래와 같은 구조일 것이다.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path d="~~~~~" />
</svg>
1. svg 스타일 컴포넌트를 생성한다.
const Svg = styled.svg`
width: 300px;
height: 300px;
path {
stroke: white;
stroke-width: 2;
}
`;
2. path를 모션 컴포넌트화 시킨다.
<motion.path
variants={svg}
initial="start"
animate="end"
d="~~~"
></motion.path>
3. path 컴포넌트 varaints를 생성한다.
const svg = {
start: { pathLength: 0, fill: "rgba(255, 255, 255, 0)" },
end: {
fill: "rgba(255, 255, 255, 1)",
pathLength: 1,
transition: {
default: { duration: 5 },
fill: { duration: 1, delay: 3 }, //fill 변화 시간을 다르게 줄 수 있다.
},
},
};
AnimatePresence
AnimatePresence는 framer에서 제공하는 컴포넌트이다.
특정 컴포넌트가 마운트되거나(나타나거나) 언마운트 될 때(사라질 때)의 애니메이션을 처리할 수 있다.
기본적인 동작은 아래와 같다.
1. AnimatePresence 컴포넌트를 생성한다.
이때 컴포넌트 내부에는 showing state에 따라 마운트, 언마운트 될 수 있도록 조건문(함수)를 둔다.
<AnimatePresence> </AnimatePresence>
2. 조건문 속에 하위 컴포넌트를 둔다.
<AnimatePresence>
{showing ? <Box/> : null}
</AnimatePresence>
3. 하위 컴포넌트의 variants를 생성하고 할당한다.
const boxVariants = {
initial: {
opacity: 0,
scale: 0,
},
visible: {
opacity: 1,
scale: 1,
rotateZ: 360,
},
leaving: {
opacity: 0,
scale: 0,
y: 50,
},
};
<AnimatePresence>
{showing ? (
<Box
variants={boxVariants}
initial="initial"
animate="visible"
exit="leaving"
/>
) : null}
</AnimatePresence>
AnimatePresence가 컴포넌트의 마운트 상태에 따라 initial, animate, exit를 각각 실행시킨다.
AnimatePresence - 슬라이더 만들기
Animate Presence를 이용하여 슬라이더를 만들 것이다.
0. 기본적인 슬라이더 구조를 만든다.
1. visible state를 생성한다.
2. 각 버튼에 setVisible을 사용한 함수를 이벤트 리스너로 단다.
3. Box의 key를 visible로 둔다.
컴포넌트는 고유의 key가 있기 때문에 key가 바뀔 경우 새로운 컴포넌트로 인식하고 리렌더링 된다.
4. variants를 생성하여 할당한다.
<Wrapper>
<AnimatePresence>
<Box
variants={boxVariants}
initial="invisible"
animate="visible"
exit="exit"
key={visible}
>
{visible}
</Box>
</AnimatePresence>
<button onClick={onNext}>next</button>
<button onClick={onPrev}>prev</button>
</Wrapper>
const boxVariants = {
invisible: {
x: 800,
opacity: 0,
scale: 0,
},
visible: {
x: 0,
opacity: 1,
scale: 1,
transition: {
duration: 2,
},
},
exit: { x: -500, opacity: 0, scale: 0, transition: { duration: 1 } },
};
이 variants는 단방향의 애니메이션만이 작동하도록 되어있다.
반대방향의 애니메이션도 동작하도록 해보자.
우리가 원하는 대로 variants를 조작할 수 있게 AnimatePresence 컴포넌트와 하위 컴포넌트에 custom props를 보낸다.
custom은 variants에 데이터를 보낼 수 있게 하는 props이다.
custom을 쓰게 되면 variants는 객체를 리턴하는 함수가 되어야 한다.
<AnimatePresence mode="wait" custom={back}>
<Box
custom={back}
variants={boxVariants}
initial="invisible"
animate="visible"
exit="exit"
key={visible}
>
{visible}
</Box>
</AnimatePresence>;
const boxVariants = {
invisible: (back: boolean) => ({
x: back ? -500 : 500,
opacity: 0,
scale: 0,
}),
visible: {
x: 0,
opacity: 1,
scale: 1,
transition: {
duration: 2,
},
},
exit: (back: boolean) => ({
x: back ? 500 : -500,
opacity: 0,
scale: 0,
transition: { duration: 1 },
}),
};
layout animation
layout animation : 레이아웃에 따른 컴포넌트 애니메이션 처리
모션 컴포넌트의 layout이라는 prop는 레이아웃이 바뀔 때마다 자동으로 애니메이션을 적용한다.
<Wrapper onClick={toggleClicked}>
<Box
style={{
justifyContent: clicked ? "center" : "flex-start",
alignItems: clicked ? "center" : "flex-start",
}}
>
<Circle layout />
</Box>
</Wrapper>;
나의 경우에는 layout prop이 제대로 작동하지 않아서 하나의 컴포넌트에서 layout 애니메이션을 처리할 때도 layoutId를 사용하였다. 작동되지 않는 이유는 찾지 못했다
shared layout animation
동일한 layoutId prop을 가졌다면 다른 컴포넌트라도 연결시켜 애니메이션을 적용시킬 수 있다.
layoutId가 있는 새 컴포넌트가 추가되고 다른 컴포넌트가 제거되면 이전 컴포넌트에서 새 컴포넌트로 레이아웃 애니메이션을 수행한다. 이는 시각적으로 하나의 연속 컴포넌트로 처리된다.
<Wrapper onClick={toggleClicked}>
<Box>
{!clicked ? (
<Circle layoutId="circle" style={{ borderRadius: 50 }} />
) : null}
</Box>
<Box>
{clicked ? (
<Circle layoutId="circle" style={{ borderRadius: 0, scale: 2 }} />
) : null}
</Box>
</Wrapper>;
layout animation - 간단한 보드 애니메이션 만들기

layout 애니메이션을 사용하면 위와 같은 예시의 보드를 간단하게 만들 수 있다.
id state가 null인지 아닌지에 따라 오버레이 컴포넌트를 마운트 시킬 수 있다.
오버레이 컴포넌트 하위에는 layoutId를 공유하는 Box 컴포넌트를 두어 애니메이션을 적용할 수 있게 한다.
function App() {
const [id, setId] = useState<null | string>(null);
return (
<Wrapper>
<Grid>
{["1", "2", "3", "4"].map((i) => (
<Box onClick={() => setId(i)} key={i} layoutId={i}></Box>
))}
</Grid>
<AnimatePresence>
{id ? (
<Overlay
onClick={() => setId(null)}
variants={overlay}
initial="hidden"
animate="visible"
exit="exit"
>
<Box layoutId={id} style={{ width: 400, height: 200 }} />
</Overlay>
) : null}
</AnimatePresence>
</Wrapper>
);
}
const overlay = {
hidden: { backgroundColor: "rgba(0, 0, 0, 0)" },
visible: { backgroundColor: "rgba(0, 0, 0, 0.5)" },
exit: { backgroundColor: "rgba(0, 0, 0, 0)" },
};
레이아웃 애니메이션의 다양한 사용 예시는 아래 링크에서 확인할 수 있다.
https://www.framer.com/docs/animate-shared-layout/#syncing-layout-animations
AnimateSharedLayout | Framer for Developers
Animate layout changes across, and between, multiple components.
www.framer.com
'Tech > TypeScript' 카테고리의 다른 글
[ TS 기초 ] 타입스크립트란 (0) | 2023.03.21 |
---|---|
[Typescript / TS] 영화 웹 서비스 만들기 (0) | 2023.02.07 |
[Typescript / TS] ToDo 칸반보드 만들기 (0) | 2023.01.23 |
[Typescript / TS] ToDo리스트 만들기 (0) | 2023.01.16 |
[Typescript / TS] Recoil을 사용한 state 관리 (0) | 2023.01.16 |