#7 ANIMATIONS

#7.1

Examples | Framer for Developers

Framer Motion

React์šฉ production-ready ๋ชจ์…˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (์˜คํ”ˆ ์†Œ์Šค)

https://www.framer.com/motion

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

Motion

Last updated

Was this helpful?