https://www.framer.com/motion/
Production-Ready Animation Library for React | Framer Motion
Framer Motion is a production-ready React animation and gesture library.
www.framer.com
예쁜 애니메이션을 만들어보자~~~
npm i framer-motion
import {motion} from 'framer-motion';
import styled from 'styled-components';
const Box = styled(motion.div)`
`;
styled-components와 함께 사용하려면
styeld(motion.태그이름)
이렇게 사용한다.
const boxVariants = {
start: {
scale: 0
},
end: {
scale: 1,
rotateZ: 360,
transistion: {
type: "spring",
mass: 1,
}
}
}
function App() {
return (
<Wrapper>
<Box variants={boxVariants} initial="start" animate="end" />
</Wrapper>
);
}
1️⃣ transition={{ ease: "easeOut", duration: 2 }}
2️⃣ transition={{ type: "spring", stiffness: 100 }}
3️⃣ transition={{ yoyo: infinity | 10 }}
transition의 type은 tween, spring, inertia 가 있다.
여기서 spring의 경우 stiffness으로 추가 설정 가능하다(스프링의 강도)
yoyo속성을 통해 변경되는 사항을 여러번 반복하여 나타낼 수 있다.
infinity입력시 무한 반복
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.1
}
}
}
const circleVariants = {
start: {
opacity: 0,
y: 10
},
end : {
opacity: 1,
y:0,
}
}
function App() {
return (
<Wrapper>
<Box variants={boxVariants} initial="start" animate = "end" >
// initial과 animate에는 boxVariant에서 설정했던 이름이 똑같이 들어가야함
<Circle variants={circleVariants}/>
<Circle variants={circleVariants}/>
<Circle variants={circleVariants}/>
<Circle variants={circleVariants}/>
</Box>
</Wrapper>
);
}
부모 컨포넌트가 자식 컴포넌트의 animation을 제어할 수도 있다.
자식 컴포넌트의 variants에 아무것도 적지 않으면 부모 컴포넌트의 variants를 그대로 가져온다
부모로부터 variant 변경 사항을 상속하지 않도록 하려면
inherit: false
로 설정해야함
딜레이 시간 후에 하위 애니메이션이 시작된다
하위 컴포넌트의 애니메이션 지속 시간만큼 시차를 둘 수 있다
예를 들어, staggerChildren이 0.01이면
첫 번째 자식은 0초,
두 번째 자식은 0.01초,
세 번째 자식은 0.02초 지연되는 식.
강의에 잠깐 나와서 추가로 적어봄
place-items (Container Properties)
justify-items과 align-items를 합친 축약형
place-self (Item Properties)
justify-self와 align-self를 합친 축약형
const boxVariants = {
hover: {scale: 1.5, rotateZ: 180},
click: {scale: 1, borderRadius: "100px"},
drag: {backgroundColor : "rgb(253, 121, 168)", transition: {duration:10 }}
}
function App() {
const biggerBoxRef = useRef<HTMLDivElement>(null)
return (
<Wrapper>
<BiggerBox ref = {biggerBoxRef}>
<Box
drag
dragSnapToOrigin
dragElastic={0}
dragConstraints={biggerBoxRef}
variants = {boxVariants}
whileHover="hover"
whileDrag = "drag"
whileTap="click"/>
</BiggerBox>
</Wrapper>
);
}
while 속성
끌기 활성화기본적으로 false로 설정됨양방향 = true 특정방향으로 = "x" or "y"
rbg값으로 해야 색이 천천히 변함
허용된 드래그 가능 영역에 제약 조건 적용
1. 픽셀 이용
dragConstraints={{top:-200, bottom: 200, left: -200, right:200}}
2. ref이용
function App() {
const biggerBoxRef = useRef<HTMLDivElement>(null)
return (
<Wrapper>
<BiggerBox ref = {biggerBoxRef}>
<Box
drag
dragConstraints={biggerBoxRef}/>
</BiggerBox>
</Wrapper>
);
}
드래그 가능 요소를 드래그를 놓을 때, 원점으로 다시 애니메이션
외부 제약 조건에서 허용되는 이동 정도(당기는 힘의 정도)
0 = 움직임 없음
1 = 전체 움직임
(기본은 0.5)
scroll하는 대로 커지고 작아지게 하는거 배우기 전에 MotionValues를 배워야함
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}}
drag = "x"
dragSnapToOrigin/>
</Wrapper>
);
}
MotionValues는 애니메이션 값의 상태(state)와 속도(velocity)를 추적한다.
MotionValue는 React State가 아니기 때문에 Motion Value값이 바뀌어도 리랜더링이 일어나지 않는다
export function MyComponent() {
const x = useMotionValue(0)
return < motion.div style={{ x }} />
}
useMotionValue 후크로 MotionValues를 생성할 수 있다.
useMotionValue에 전달된 값은Motion Value의 초기 상태로 작동
x.set(100)
x.get() // 100
set으로 설정하고 get으로 값을 읽을 수 있음~!
useTransform 훅을 통해 MotionValues를 연결한다
useTransform()는 한 값 범위에서 다른 값 범위로 매핑하여 다른 MotionValue의 output을 변환하는 MotionValue를 만든다
x(Motion Value)값을 다른 output값으로 변환해준다.
ex) x: -400 => 1
const input = [-200, 0, 200]
const output = [0, 1, 0]
const opacity = useTransform(x, input, output)
return < motion.div drag="x" style={{ x, opacity }} />
이제 scroll을 해보자~!
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))",
])
const {scrollYProgress} = useViewportScroll();
const scale = useTransform(scrollYProgress, [0,1], [1,5])
return (
<Wrapper style={{background: gradient}}>
<Box
style = {{x, rotateZ , scale}}
drag = "x"
dragSnapToOrigin/>
</Wrapper>
);
}
뷰포트가 스크롤될 때 업데이트 되는 MotionValues를 리턴해줌
scrollX: 실제 수평 스크롤 픽셀 ex) 500px
scrollY: 실제 수직 스크롤 픽셀 ex) 500px
scrollXProgress : 0 ~ 1 사이의 수평 스크롤
scrollYProgress : 0 ~ 1 사이의 수직 스크롤(가장 상단 0, 가장 하단 1)
Font Awesome
The world’s most popular and easiest to use icon set just got an upgrade. More icons. More styles. More Options.
fontawesome.com
각종 로고 모여있는 사이트
const Svg = styled.svg`
width: 300px;
height: 300px;
path {
stroke:white;
strokeWidth: 2;
}
`;
const svgVar = {
start: {
pathLength: 0,
fill: "rgba(255,255,255,0)"
},
end: {
pathLength:1,
fill: "rgba(255,255,255,1)",
transition: {
default : {duration : 3},
fill : {duration : 1, delay: 1}
}
}
}
function App() {
return (
<Wrapper >
<Svg xmlns="http://www.w3.org/2000/Svg" viewBox="0 0 448 512">
<motion.path
variants={svgVar}
initial = "start"
animate = "end"
d="M224 373.12c-25.24-31.67-40.08-59.43-45-83.18-22.55-88 112.61-88 90.06 0-5.45 24.25-20.29 52-45 83.18zm138.15 73.23c-42.06 18.31-83.67-10.88-119.3-50.47 103.9-130.07 46.11-200-18.85-200-54.92 0-85.16 46.51-73.28 100.5 6.93 29.19 25.23 62.39 54.43 99.5-32.53 36.05-60.55 52.69-85.15 54.92-50 7.43-89.11-41.06-71.3-91.09 15.1-39.16 111.72-231.18 115.87-241.56 15.75-30.07 25.56-57.4 59.38-57.4 32.34 0 43.4 25.94 60.37 59.87 36 70.62 89.35 177.48 114.84 239.09 13.17 33.07-1.37 71.29-37.01 86.64zm47-136.12C280.27 35.93 273.13 32 224 32c-45.52 0-64.87 31.67-84.66 72.79C33.18 317.1 22.89 347.19 22 349.81-3.22 419.14 48.74 480 111.63 480c21.71 0 60.61-6.06 112.37-62.4 58.68 63.78 101.26 62.4 112.37 62.4 62.89.05 114.85-60.86 89.61-130.19.02-3.89-16.82-38.9-16.82-39.58z"/>
</Svg>
</Wrapper>
);
}
0부터 1까지 값을 가짐
transition: {
default : {duration : 3},
fill : {duration : 1, delay: 1}
}
transition 속성에서 defalut로 다른 모든 속성에 설정하고
!특정! 속성에만 다르게 설정할 수도 있음
AnimatePresence를 사용하면 React 트리에서 컴포넌트가 제거될 때 제거되는 컴포넌트에 애니메이션 효과를 줄 수 있다. React에는 수명 주기 메서드가 없기 때문에 종료 애니메이션(exit)을 활성화해야 한다
const boxVariants = {
entry: (back: boolean) => ({
x: back ? -200 : 200,
opacity: 0,
scale: 0,
}),
center: {
x: 0,
opacity: 1,
scale: 1,
transition: {
duration: 1,
},
},
exit: (back:boolean) => ({
x: back ? 200 : -200,
opacity: 0,
scale: 0,
transition: {
duration: 1
}
})
};
function App() {
const [visible, setVisible] = useState(1);
const [back , setBack] = useState(false);
const nextPlease = () => {
setVisible(prev => prev === 10? 10 : prev + 1)
setBack(false);
};
const prevPlease = () => {
setVisible(prev => prev === 1 ? 1 : prev - 1)
setBack(true);
};
return (
<Wrapper>
<AnimatePresence custom={back}>
{[1,2,3,4,5,6,7,8,9,10].map(i => i === visible ?
<Box
custom={back}
key = {visible}
variants={boxVariants}
initial = "entry"
animate="center"
exit = "exit" >
{visible}
</Box> : null)}
</AnimatePresence>
<button onClick = {nextPlease}>NEXT</button>
<button onClick = {prevPlease}>PREV</button>
</Wrapper>
);
}
각 애니메이션 컴포넌트에 대해 동적 variants를 다르게 적용할 때 사용할 수 있는 사용자 지정 데이터
const variants = {
visible: (custom) => ({
opacity: 1,
transition: { delay: custom * 0.2 }
})
}
<motion.div custom={0} animate="visible" variants={variants}/>
<motion.div custom={1} animate="visible" variants={variants}/>
<motion.div custom={2} animate="visible" variants={variants}/>
true로 설정되면 AnimatePresence는 한번에 하나의 컴포넌트만 랜더링 함
즉 entry 가 다 끝나면 exit이 실행됨
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>
);
}
개쩐다...
true인 경우 해당 컴포넌트의 레이아웃이 변경될 때 새 위치에 자동으로 애니메이션이 적용됨
<motin.div lyaout/>
layoutid prop을 가진 모션 컴포넌트들 간에 애니메이션을 적용할 수 있음
layoutId가 있는 새 컴포넌트가 추가되고 다른 컴포넌트가 제거되면 이전 컴포넌트에서 새 컴포넌트로 레이아웃 애니메이션을 수행함
시각적으로 하나의 연속 컴포넌트로 처리됨!! 멋져
[React] Recoil Selector의 get, set 사용법 (0) | 2022.10.22 |
---|---|
[모각코][React] useQuery (0) | 2022.08.07 |
[React] React Hook Form (0) | 2022.07.15 |
[React] Recoil로 상태 관리 하기 (0) | 2022.07.14 |
[React] ThemeProvider (0) | 2022.07.09 |