#7 ANIMATIONS
#7.1
Examples | Framer for Developers
Framer Motion
React์ฉ production-ready ๋ชจ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (์คํ ์์ค)
https://github.com/framer/motion
npm i framer-motion
import { motion } from "framer-motionโ
motion ์ฌ์ฉ์ ๊ธฐ์กด์ <div> ์ ์ฌ์ฉํ ์ ์์
<motion.div> ๋ก ์ฌ์ฉํด์ผํจ
CRACO
CRACO(Create React App Configuration Override)๋ create-react-app์ ์ํ ์ฝ๊ณ ์ดํดํ๊ธฐ ์ฌ์ด configuration ๋ ์ด์ด์ ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ root์ ๋จ์ผ configuration(์: craco.config.js) ํ์ผ์ ์ถ๊ฐํ๊ณ eslint, babel, postcss configuration ๋ฑ์ ์ฌ์ฉ์ ์ง์ ํ์ฌ 'eject'๋ฅผ ์ฌ์ฉํ์ง ์๊ณ create-react-app ๋ฐ ์ฌ์ฉ์ ์ง์ ์ ๋ชจ๋ ์ด์ ์ ์ป์ผ์ญ์์ค.
https://github.com/gsoft-inc/craco
https://github.com/gsoft-inc/craco/blob/master/packages/craco/README.md#installation
craco.config.js
module.exports = {
webpack: {
configure: {
module: {
rules: [
{
type: 'javascript/auto',
test: /\\.mjs$/,
include: /node_modules/,
},
],
},
},
},
}
#7.2 Animation
Framer Motion์ ์ ๋๋ฉ์ด์ ์ ๋ชจ์ ์ปดํฌ๋ํธ์ ์ ์ฐํ animate ์์ฑ์ ํตํด ์ ์ด๋๋ค.
๊ฐ๋จํ ์ ๋๋ฉ์ด์ ์ animate props์์ ์ง์ ๊ฐ์ ์ค์ ํ ์ ์๋ค.
initial - ์์ / transition - ๋ณํ ๋ฐฉ๋ฒ / animate - ์ข ๋ฃ
#7.3 Variants part
Variants ์ฌ๋ฌ ์ ๋๋ฉ์ด์ ์ ํฉ์น ์ ์์ & ๊น๋ํ ์ฝ๋
const variant = {
start: {},
end: {}
}
motion.div variant={variant} initial=โstartโ end=โendโ
#7.4
๋ถ๋ชจ ์์์ ์ ๋๋ฉ์ด์ ๊ณผ props๋ฅผ ์์ ์์์๋ ์ ๋ฌํ๋ค.
& ๊ฐ ์์ ์์์ ๊ฐ๊ฐ ๋๋ ์ด ์ฃผ๋ ๋ฑ์ ๋ฏธ์น ๊ธฐ๋ฅ๋ ๊ฐ๋ฅํ๋ค.
delayChildren: ๋๋ ์ด ์๊ฐ(์ด) ํ ํ์ ์ ๋๋ฉ์ด์ ์์
staggerChildren: ํ์ ์ปดํฌ๋ํธ์ ์ ๋๋ฉ์ด์ ์ ์ง์ ์๊ฐ(์ด)๋งํผ ์์ฐจ ์๋ฅผ ๋ค์ด, staggerChildren์ด 0.01์ด๋ฉด ์ฒซ ๋ฒ์งธ ์์์ 0์ด, ๋ ๋ฒ์งธ ์์์ 0.01์ด, ์ธ ๋ฒ์งธ ์์์ 0.02์ด ์ง์ฐ. ๊ณ์ฐ๋ stagger ๋๋ ์ด๊ฐ delayChildren์ ์ถ๊ฐ๋๋ค.
inherit: boolean
๋ถ๋ชจ๋ก๋ถํฐ variant ๋ณ๊ฒฝ ์ฌํญ์ ์์ํ์ง ์๋๋ก ํ๋ ค๋ฉด false๋ก ์ค์ .
#7.5 Gestures
whileHover - ํธ๋ฒ ์ค์ผ๋ ์๋ํ ์ ๋๋ฉ์ด์
whileTap - ํด๋ฆญ ์ค์ผ๋ ์๋ํ ์ ๋๋ฉ์ด์
Drag
drag: boolean | โxโ | โyโ
๋๊ธฐ ํ์ฑํ- ๊ธฐ๋ณธ false ์๋ฐฉํฅ ๋๋๊ทธ๋ฅผ ์ํ๋ฉด true, ํน์ ๋ฐฉํฅ์ผ๋ก ํ๋ ค๋ฉด x or y ์ค์
whileDrag - ๋๋๊ทธ ์ค์ผ๋ ์๋ํ ์ ๋๋ฉ์ด์
dragConstraints - ๋๋๊ทธ๊ฐ ํ์ฉ๋๋ ๋ฒ์ (1. ํฝ์ ํฌ๊ธฐ ์ด์ฉ 2. ref ์ด์ฉ)
dragSnapToOrigin - ์๋ ์๋ฆฌ๋ก ๋์๊ฐ
dragElastic - ์ธ๋ถ ์ ์ฝ ์กฐ๊ฑด์์ ํ์ฉ๋๋ ์ด๋ ์ ๋ 0=์์ง์ ์์ 1=์ ์ฒด ์์ง์ ๊ธฐ๋ณธ 0.5 false๊ฐ๋ฅ
#7.7 MotionValues
ํน์ ๊ฐ์ ์ถ์ ํ์ง๋ง state๊ฐ ์๋๋ค?!
.set() .get() ๋ฉ์๋๋ก ๊ฐ์ ์ ๋ฐ์ดํธํ๊ฑฐ๋ ์ง์ ํ ์ ์๋ค.
#7.8 useTransform
ํน์ ๊ฐ์ ๋ค๋ฅธ ๊ฐ์ผ๋ก ๋ณํํด์ค๋ค.
useTransform(x, input, output) โ x์ ๊ฐ์ input ๋ฒ์์์ output ๋ฒ์๋ก ๋ณํํ๋ค.
#7.9 useScroll
๋ทฐํฌํธ๊ฐ ์คํฌ๋กค ๋ ๋ ์ ๋ฐ์ดํธ๋๋ MotionValues ๊ฐ์ ๋ฆฌํดํ๋ค.
scrollX - ์ค์ ์ํ ์คํฌ๋กค ํฝ์
scrollY - ์ค์ ์์ง ์คํฌ๋กค ํฝ์
scrollXProgress - 0~1 ์ฌ์ด์ ์ํ ์คํฌ๋กค
scrollYProgress - 0~1 ์ฌ์ด์ ์์ง ์คํฌ๋กค
์ด ๊ฐ์ ์ด์ฉํด ์คํฌ๋กค ์์น์ ๋ฐ๋ผ ์ ๋๋ฉ์ด์ ์ ์ถ๊ฐํ ์ ์์ !!
#7.a package.json ํ๋ฒ์ ์
๋ฐ์ดํธํ๊ธฐ
npm-check-updates ์ฌ์ฉ
npm install -g npm-check-updates // ํจํค์ง ์ค์น
ncu -u // ์ ๋ฐ์ดํธ ๊ฐ๋ฅํ ํญ๋ชฉ ํ์ธ
npm install // ์ ๋ฐ์ดํธ
#7.10 SVG Animation
fontAwsome ์์ svg ํ์ผ ๊ฐ์ ธ์ค๊ธฐ
path(SVG) โ ๋ชจ์์ ์ ์ํ๋ ์๋ฆฌ๋จผํธ
transition ๊ฐ๊ฐ ์ค ์ ์์ ใ ใ ใท !
Line drawing
svg ์๋ฆฌ๋จผํธ์ 'pathLength', 'pathSpacing', 'pathOffset' ์์ฑ์ ์ฌ์ฉํ์ฌ Line drawing ์ ๋๋ฉ์ด์ ์ ๋ง๋ค ์ ์์ต๋๋ค.
https://www.framer.com/docs/examples/#line-drawing
path (SVG)
path SVG ์๋ฆฌ๋จผํธ๋ ๋ชจ์์ ์ ์ํ๋ ์ผ๋ฐ ์๋ฆฌ๋จผํธ์ ๋๋ค.๋ชจ๋ ๊ธฐ๋ณธ ๋ชจ์์ path ์๋ฆฌ๋จผํธ๋ก ๋ง๋ค ์ ์์ต๋๋ค.
path์ ์์ฑ d๋ ๊ฒฝ๋ก์ ๋ชจ์์ ์ ์ํฉ๋๋ค.
https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
Path
motion.path ์ปดํฌ๋ํธ๋ ์ธ ๊ฐ์ง ๊ฐ๋ ฅํ SVG path ์์ฑ์ธ pathLength, pathSpacing ๋ฐ pathOffset์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์๋ ๊ฒฝ๋ก ์ธก์ ์ด ํ์ ์์ด ๋ชจ๋ 0๊ณผ 1 ์ฌ์ด์ ๊ฐ์ผ๋ก ์ค์ ๋ฉ๋๋ค.
Line drawing
์ ๊ทธ๋ฆฌ๊ธฐ ์ ๋๋ฉ์ด์ ์ pathLength, pathSpacing ๋ฐ pathOffset์ ์ธ ๊ฐ์ง ํน์ ์์ฑ์ ์ฌ์ฉํ์ฌ ๋ง์ SVG ์์๋ก ๋ง๋ค ์ ์์ต๋๋ค.
ex) motion.circle initial={{ pathLength: 0 }} animate={{ pathLength: 1 }}
https://www.framer.com/docs/examples/#line-drawing
์ฝ๋
const Svg = styled.svg` width: 300px; height: 300px; color: white; stroke: white; stroke-width:3; ` const svg = { start: { fill: "rgba(255,255,255,0)", pathLength: 0 }, end: { fill: "rgba(255, 255, 255, 1)", pathLength: 1, } } return ... <Svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 488 512"> <motion.path variants={svg} initial="start" animate="end" transition={{ default: { duration: 3 }, fill: { duration: 2, delay: 3 } }} d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z" /> </Svg>
#7.11 AnimatePresence
์กฐ๊ฑด๋ถ showing
<AnimatePresence></AnimatePresence> ์ฌ์ด์ ์กฐ๊ฑด๋ถ ๋ฑ์ฅ&์ญ์ ๊ธฐ๋ฅ์ด ์์ผ๋ฉด animate ๊ธฐ๋ฅ
initial=โโ, animate=โโ, exit=โโ
exit - ์ฌ๋ผ์ง๋ ๋ฐ์ํ ์ ๋๋ฉ์ด์
์ฝ๋
import { AnimatePresence } from "framer-motion"; const boxxVariants = { start: { opacity: 0, scale: 0 }, end: { opacity: 1, scale: 1, rotateZ: 360 }, leaving: { opacity: 0, scale: 0, y: 50 } } function ... const [showing, setShowing] = useState(false); const toggleShowing = () => setShowing((prev) => !prev) return ... <button onClick={toggleShowing}>Click</button> <AnimatePresence > {showing ? <Box variants={boxxVariants} initial="start" animate="end" exit="leaving" /> : null} </AnimatePresence>
#7.12 ~ 13 Slider
next
prev
#7.13
custom
๊ฐ ์ ๋๋ฉ์ด์ ์ปดํฌ๋ํธ์ ๋ํด ๋์ variants ๋ฅผ ๋ค๋ฅด๊ฒ ์ ์ฉํ ๋ ์ฌ์ฉํ๋ ์ฌ์ฉ์ ์ง์ ๋ฐ์ดํฐ
์ฝ๋
const Box = styled(motion.div)` width: 400px; height: 200px; display: flex; justify-content: center; align-items: center; background-color: rgba(255, 255, 255, 1); border-radius: 40px; font-size: 28px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06); position: absolute; top:100px; ` const box = { start: (back: boolean) => ({ x: back ? -500 : 500, opacity: 0, scale: 0, transition: { duration: 0.5 } }), end: { x: 0, opacity: 1, scale: 1, transition: { duration: 0.5, } }, exit: (back: boolean) => ({ x: back ? 500 : -500, opacity: 0, scale: 0, transition: { duration: 0.5 } }), } function ... const [visible, setVisible] = useState(1); const [back, setBack] = useState(false); const nextPlease = () => { setBack(false); setVisible((prev) => (prev === 10 ? 1 : prev + 1)) } const prevPlease = () => { setBack(true); setVisible((prev) => (prev === 1 ? 10 : prev - 1)) } return ... <AnimatePresence custom={back} mode="wait"> <Box key={visible} custom={back} variants={box} initial="start" animate="end" exit="exit">{visible}</Box> </AnimatePresence> <button onClick={prevPlease}>prev</button> <button onClick={nextPlease}>next</button>
#7.14
์ ๋๋ฉ์ด์ ์ ์ ์ฉํ์ง ์์๋ ์์์ ์ ์ฉํจ
layout
๋ณํ ๋ฐ์ํ๋ฉด ์๋ animation ์ ์ฉ
Animate between components
๋์ผํ layoutId prop์ ๊ฐ์ง ๋ชจ์ ์ปดํฌ๋ํธ ๊ฐ ์ ๋๋ฉ์ด์ ์ ์ ์ฉํ๋ค.
์ฝ๋
const Circle = styled(motion.div)` background-color: #00a5ff; height: 100px; width: 100px; border-radius: 50px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06); ` function ... const [clicked, setClicked] = useState(false) const toggleClicked = () => setClicked((prev) => !prev) return ... <Wrapper onClick={toggleClicked} style={{ background: gradient }}> <Box> {!clicked ? <Circle layoutId="circle" /> : null} </Box> <Box> {clicked ? <Circle layoutId="circle" /> : null} </Box> </Wrapper>
#7.15
#7.16
Last updated
Was this helpful?