가수면
[Framer Motion] Framer Motion 본문
npm install framer-motion
애니메이션
scaleX: 가로 크기
transform-origin: 요소 변형의 원점을 설정
x: 가로 좌표
y: 세로 좌표
pathLength: 테두리 길이 (initial: 0, animate: 1로 설정해줄 경우 테두리가 서서히 그려짐)
transtion
type
ㄴspring: 통통 튕김(x 또는 scale은 spring이 자동 적용 됨)
ㄴtween: spring효과 없이 딱 멈춤(opacity 또는 color는 tween이 자동 적용 됨)
ㄴmass: 무게감
bounce: type: "spring"일 때 튕김 정도(탄력), 0 ~ 1 사이값
damping: 반대힘. 0으로 설정하면 스프링이 무한정 진동
duration: 시작부터 끝까지 실행되는 시간
delay : 시작되기까지 시간
애니메이션 반복 시키기
배열을 설정해주고 transition으로 repeat 횟수를 설정해주면 해당 배열값이 반복됨
const logoVariants = {
normal: {
fillOpacity: 1,
},
active: {
fillOpacity: [0, 1, 0],
transition: {
repeat: Infinity,
},
},
};
Variants
자동완성 기능 활성화시킬 수 있음
import { motion, Variants } from "framer-motion";
// ...
const variants: Variants = {
start: {},
end: {}
}
기본 형식
줄줄이 props로 길게 늘어놓을 필요 없이 props를 선언해 뽑아쓸 수 있음
const myVars = {
start: { scale: 0 },
end: { scale: 1, rotateZ: 360, transition: { type: "spring", delay: 0.5 } },
};
function App() {
return (
<Wrapper>
<Box variants={myVars} initial="start" animate="end" />
</Wrapper>
);
자식 컴포넌트
자식들의 애니메이션을 지정해줄 때 하나하나 지정해주지 않아도 됨
(※ initial과 animate의 이름이 같아야 함 )
const boxVariants = {
start: {
// ...
},
end: {
// ...
delayChildren: 0.5,
staggerChildren: 0.2,
},
},
};
const circleVariants = {
start: {
// ...
},
end: {
// ...
},
};
<Box variants={boxVariants} initial="start" animate="end">
<Circle variants={circleVariants} />
<Circle variants={circleVariants} />
<Circle variants={circleVariants} />
<Circle variants={circleVariants} />
</Box>
커스텀
custom을 설정하면 매개변수로 받아 Variants를 사용할 수 있음
const box = {
entry: (isBack: boolean) => ({
x: isBack ? -500 : 500,
opacity: 0,
scale: 0,
}),
center: {
x: 0,
opacity: 1,
scale: 1,
transition: {
duration: 0.3,
},
},
exit: (isBack: boolean) => ({
x: isBack ? 500 : -500,
opacity: 0,
scale: 0,
transition: { duration: 0.3 },
}),
};
// ...
const [back, setBack] = useState(false);
// ...
<AnimatePresence>
<Box custom={back} variants={box} initial="entry" animate="center" exit="exit" key={visible}>
{visible}
</Box>
</AnimatePresence>
Gestures
마우스 이벤트
만약 자식 컴포넌가 있다면 따로 지정해주지 않아도 자식 컴포넌트에 마우스 이벤트가 저절로 상속된다.
※ 드래그 시 색상을 rgb로 설정해주면 transition을 따로 설정해주지 않아도 약간의 duration이 적용된다.
const boxVariants = {
hover: { scale: 1.5, rotateZ: 90 },
click: { scale: 1, borderRadius: "100px" },
drag: { backgroundColor: "rgb(46, 204, 113)", transition: { duration: 10 } },
};
function App() {
return (
<Wrapper>
<Box
drag
variants={boxVariants}
whileHover="hover"
whileDrag="drag"
whileTap="click"
/>
</Wrapper>
);
x, y로 고정
// ...
<Box
drag="x"
// ...
드래그 영역 제한하기
function App() {
const biggerBoxRef = useRef<HTMLDivElement>(null);
return (
<Wrapper>
<BiggerBox ref={biggerBoxRef}>
<Box
drag
// 드래그 놓았을 때 정가운데로
dragSnapToOrigin
// 마우스 저항도 0~1사이값
dragElastic={0}
// dragConstraints={{ top: number, bottom: number, left: number, right: number }}가 기본 형태.
// 0, 0, 0, 0으로 설정하면 드래그를 놓았을 때 가운데로 이동한다.
// useRef값을 넣어주면 좌표값이 자동으로 계산됨
dragConstraints={biggerBoxRef}
variants={boxVariants}
whileHover="hover"
whileTap="click"
/>
</BiggerBox>
</Wrapper>
);
}
좌표값 다루기
useMotionValueEvent
사용 가능한 이벤트는 다음과 같다.
- change
- animationStart
- animationComplete
- animationCancel
function App() {
const x = useMotionValue(0);
useMotionValueEvent(x, "animationStart", () => {
console.log("animation started on x")
})
useMotionValueEvent(x, "change", (latest) => {
console.log("x changed to", latest)
})
return (
<Wrapper>
<Box style={{ x }} drag="x" dragSnapToOrigin />
</Wrapper>
);
}
useTransform
좌표값을 임의의 다른 값으로 치환
function App() {
const x = useMotionValue(0);
const test = useTransform(x, [-800, 0, 800], [2, 1, 0.1]);
return (
<Wrapper>
<Box style={{ x, scale: test }} drag="x" dragSnapToOrigin />
</Wrapper>
);
}
응용 (x값에 따른 그래디에이션 배경색)
function App() {
const x = useMotionValue(0);
const rotateZ = useTransform(x, [-800, 800], [-360, 360]);
const gradient = useTransform(
x,
[-800, 800],
[
"linear-gradient(135deg, rgb(0, 210, 238), rgb(0, 83, 238))",
"linear-gradient(135deg, rgb(0, 238, 155), rgb(238, 178, 0))",
]
);
return (
<Wrapper style={{ background: gradient }}>
<Box style={{ x, rotateZ, scale }} drag="x" dragSnapToOrigin />
</Wrapper>
);
}
useScroll
scrollX / Y: 픽셀 단위 값.
scrollX / YProgress : 0~1사이 값.(퍼센티지)
const { scrollYProgress } = useScroll();
useMotionValueEvent(scrollYProgress, "change", (latest) => {
console.log(latest);
});
좌표값에 따른 스타일 변경
// ...
const { scrollYProgress } = useScroll();
const scale = useTransform(scrollYProgress, [0, 1], [1, 5]);
return (
<Wrapper>
<Box style={{ x, scale }} drag="x" dragSnapToOrigin />
</Wrapper>
);
}
스크롤 값을 연산하려면 .get()을 붙여주어야 함.
// top: scrollY + 100을 아래와 같이 변경
top: scrollY.get() + 100,
특정 속성 애니메이션 지정하기
ex) 테두리색이 채워진 후에 배경색을 채우고자 할 경우
transition={{
default: { duration: 5 },
// scale이든 뭐든 상관없음
fill: { duration: 1, delay: 3 },
}}
AnimatePresence
사라질 때의 애니메이션을 지정 가능하도록 해주는 태그
기본
state에 따라 나타나고 사라지는 컴포넌트를 제어
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>
onExitComplete
애니메이션이 끝나면 실행될 함수
initial={false}
첫 시작 애니메이션 비활성화
<AnimatePresence initial={false} onExitComplete={toggleNextSlice}>
// ...
</AnimatePresence>
Layout
기본
컴포넌트에 layout 설정해주는 것만으로도 애니메이션이 적용 됨.
function App() {
const [clicked, setClicked] = useState(false);
const toggleClicked = () => setClicked((prev) => !prev);
return (
<Wrapper onClick={toggleClicked}>
<Box style={{ justifyContent: clicked ? "center" : "flex-start", alignItems: clicked ? "center" : "flex-start" }}>
<Circle layout />
</Box>
</Wrapper>
);
}
서로 다른 컴포넌트를 애니메이션으로 연결하고싶다면 layoutId를 지정해주면 됨
function App() {
const [clicked, setClicked] = useState(false);
const toggleClicked = () => setClicked((prev) => !prev);
return (
<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>
);
}
useAnimation
길어지는 애니메이션을 훅으로 뺄 수 있음.
예시) animate={{ scaleX: isSearch ? 1 : 0 }}에 useAnimation을 적용해 바꿔보기
const inputAnimation = useAnimation();
const toggleSearch = () => {
if (searchOpen) {
// variants를 설정해주었다면 inputAnimation.start("start") 식으로 해도 됨
inputAnimation.start({
scaleX: 0,
});
} else {
inputAnimation.start({ scaleX: 1 });
}
setSearchOpen((prev) => !prev);
};
// ...
animate={inputAnimation}
'React > 라이브러리' 카테고리의 다른 글
react-markdown (0) | 2023.06.10 |
---|---|
Tailwind CSS (1) | 2023.06.08 |
React beautiful dnd (0) | 2023.03.23 |
[React Query] 비동기 취소하기 (0) | 2023.01.12 |
[React Query] 인증 (0) | 2023.01.11 |