Skip to content

singsoong/framer_practice

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

17 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Framer-motion

๐Ÿ“Œ Installation

$ yarn add framer-motion

๐Ÿ“Œ Usage

๐Ÿ“„ Declaration

<motion.div>Box</motion.div>
  • styled-components
const Box = styled(motion.div)``;

or

<Box as={motion.div} />
  • Basic Animation
<Box
  transition={{ type: "spring", delay: 0.3 }}
  initial={{ scale: 0 }}
  animate={{ scale: 1, rotateZ: 360 }}
/>

๐Ÿ“„ Variants

// Variants
const myVars = {
  start: { scale: 0 },
  end: { scale: 1, rotateZ: 360, transition: { type: "spring", delay: 0.3 } },
};

<Box variants={myVars} initial="start" animate="end" />;

๐Ÿ“„ Orchestration

  • delayChildren: ๋ชจ๋“  ์ž์‹๋“ค์—๊ฒŒ delay ๊ฑฐ๋Š” ์†์„ฑ
  • staggerChildren: ๊ฐ ์ž์‹๋งˆ๋‹ค ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์†์„ฑ
// Variants
const boxVariants = {
  start: {
    opacity: 1,
    scale: 0,
  },
  end: {
    opacity: 1,
    scale: 1,
    transition: {
      delayChildren: 0.3,
      staggerChildren: 0.2,
    },
  },
};

const circleVariants = {
  start: {
    opacity: 0,
    y: 20,
  },
  end: {
    opacity: 1,
    y: 0,
  },
};

function App() {
  return (
    <Container>
      <Box variants={boxVariants} initial="start" animate="end">
        <Circle variants={circleVariants} />
        <Circle variants={circleVariants} />
        <Circle variants={circleVariants} />
        <Circle variants={circleVariants} />
      </Box>
    </Container>
  );
}
  • whileHover, whileTap๊ณผ ๊ฐ™์€ props๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์กฐ๊ฑด์—์„œ์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ.
const boxVariants = {
  hover: { scale: 1.5, rotateZ: 90 },
  tap: { borderRadius: "150px", scale: 1 },
};

function App() {
  return (
    <Container>
      <Box variants={boxVariants} whileHover="hover" whileTap="tap"></Box>
    </Container>
  );
}

๐Ÿ“„ Drag

  • drag ์†์„ฑ์„ ์ค˜์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ drag ํ•  ์ˆ˜ ์žˆ์Œ
<Box drag variants={boxVariants} whileHover="hover" whileTap="tap" />

์ปดํฌ๋„ŒํŠธ ์• ๋‹ˆ๋ฉ”์ด์…˜์— color๋ฅผ ์ค„ ๋•Œ blue, black๊ณผ ๊ฐ™์ด string ํ˜•ํƒœ๋กœ ์ฃผ๋ฉด transition์ด ๋™์ž‘์•ˆํ•จ. ๊ฐ’์œผ๋กœ ์ž…๋ ฅํ•ด์ฃผ์–ด์•ผ ํ•จ. (rgba(1, 2, 3)๊ณผ ๊ฐ™์ด.)

  • drag constraints: dragConstraints ์†์„ฑ์„ ํ†ตํ•ด ๋“œ๋ž˜๊ทธ ์˜์—ญ์„ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Œ
    • ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•  ๋•Œ top, left, bottom, right์™€ ๊ฐ™์ด ๊ฐ’์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Œ
    • element์˜ ref๋ฅผ ๊ฐ€์ ธ์™€์„œ dragConstraints์˜ ๊ฐ’์œผ๋กœ ์ฃผ์–ด ์ œํ•œํ•  ์ˆ˜๋„ ์žˆ์Œ
// value
<Box
  drag
  dragConstraints={{ top: -100, bottom: 100, left: -100, right: 100 }}
  dragSnapToOrigin
  variants={boxVariants}
  whileHover="hover"
  whileTap="tap"
  whileDrag={{
    backgroundColor: "rgba(46, 204, 113)",
    transition: { duration: 2 },
  }}
/>;

// ref
function App() {
  const bigBoxRef = useRef < HTMLDivElement > null;
  return (
    <Container>
      <BigBox ref={bigBoxRef}>
        <Box
          drag
          dragConstraints={bigBoxRef}
          dragSnapToOrigin
          variants={boxVariants}
          whileHover="hover"
          whileTap="tap"
          whileDrag={{
            backgroundColor: "rgba(46, 204, 113)",
            transition: { duration: 2 },
          }}
        />
      </BigBox>
    </Container>
  );
}
  • dragSnapToOrigin ์†์„ฑ์„ ์ฃผ๋ฉด ๋“œ๋ž˜๊ทธ ํ›„ ์ œ์ž๋ฆฌ๋กœ ๋Œ์•„๊ฐ
<Box
  drag
  dragConstraints={bigBoxRef}
  dragSnapToOrigin
  variants={boxVariants}
  whileHover="hover"
  whileTap="tap"
  whileDrag={{
    backgroundColor: "rgba(46, 204, 113)",
    transition: { duration: 2 },
  }}
/>
  • dragElastic: ๋“œ๋ž˜๊ทธํ•  ๋•Œ, ๋‹น๊ธฐ๋Š” ํž˜์„ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ์Œ (default: 0.5)
<Box
  drag
  dragConstraints={bigBoxRef}
  dragSnapToOrigin
  dragElastic={1}
  variants={boxVariants}
  whileHover="hover"
  whileTap="tap"
  whileDrag={{
    backgroundColor: "rgba(46, 204, 113)",
    transition: { duration: 2 },
  }}
/>

๐Ÿ“„ MotionValues

  • useMotionValue๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ’์„ ์„ ์–ธํ•˜๊ณ , ์ด ๊ฐ’์„ ์ถ”์ ํ•  element์™€ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Œ
  • ์ถ”์ ํ•  ๋•Œ์—๋Š” useMotionValueEvent๋ฅผ ์‚ฌ์šฉ
  • x์˜ ๊ฐ’์ด ๋ณ€ํ•  ๋•Œ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฆฌ๋ Œ๋”๋ง ๋˜๋Š” ๋ฐฉ์‹์ด ์•„๋‹˜, ๋‹จ์ˆœํžˆ x์˜ ๊ฐ’๋งŒ ๋ฐ”๋€œ
const x = useMotionValue(0);

useMotionValueEvent(x, "change", (value) => {
  console.log(value);
});

return (
  <Container>
    <Box style={{ x }} drag="x" dragSnapToOrigin />
  </Container>
);

๐Ÿ“„ useTransform

  • useTransform์€ ์ถ”์ ํ•  ๊ฐ’์— ๋”ฐ๋ผ ๊ฐ’์„ ๋ณ€๊ฒฝ์‹œ์ผœ์คŒ
  • ์˜ˆ๋ฅผ ๋“ค์–ด, x์˜ ์ขŒํ‘œ์— ๋”ฐ๋ผ scale์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์—ˆ์œผ๋ฉด ํ•จ
    • x๊ฐ’์ด -300์ผ ๋•Œ 2, 0์ผ ๋•Œ 1, 300์ผ ๋•Œ 0.1์ด ๋ฆฌํ„ด๋˜์—ˆ์œผ๋ฉด ํ•จ
  • ์ฒซ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ถ”์ ํ•  ๊ฐ’, ๋‘๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ถ”์ ํ•  ๊ฐ’์˜ ๊ธฐ์ค€๊ฐ’, ์„ธ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๊ธฐ์ค€๊ฐ’์— ๋„๋‹ฌํ–ˆ์„ ๋•Œ ํ• ๋‹นํ•  ๊ฐ’์„ ์ž…๋ ฅ
    • ๋”ฐ๋ผ์„œ input๊ณผ output์˜ ๊ฐœ์ˆ˜๊ฐ€ ๊ฐ™์•„์•ผํ•จ
const x = useMotionValue(0);
const scale = useTransform(x, [-300, 0, 300], [2, 1, 0.1]);

useScroll

  • scrollX, scrollY: ํ”ฝ์…€ ๋‹จ์œ„๋กœ ์Šคํฌ๋กค ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ
  • scrollXProgress, scrollYProgress: ์ด ์Šคํฌ๋กค ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’์˜ ๋น„์œจ์„ ๋ฆฌํ„ด (์ตœ์†Œ๊ฐ’ 0, ์ตœ๋Œ€๊ฐ’ 1)

๐Ÿ“„ SVG Animation

  • motion์˜ pathํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ animation์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ์Œ
  <motion.path initial={...} animate={...} />
  • ๊ฐ animation์˜ transition์„ ๋”ฐ๋กœ๋”ฐ๋กœ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ์Œ
<motion.path
  variants={svgVar}
  initial="start"
  animate="end"
  transition={{
    default: { duration: 3 },
    fill: { duration: 2, delay: 3 },
  }}
  stroke="white"
  strokeWidth="2"
/>

๐Ÿ“„ AnimatePresence

  • AnimatePresence๋ฅผ ์‚ฌ์šฉํ•ด์„œ React ํŠธ๋ฆฌ์—์„œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ์ œ๊ฑฐ๋  ๋•Œ์˜ animation์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ์Œ
  • exit ์†์„ฑ์„ ํ†ตํ•ด ์ข…๋ฃŒ๋  ๋•Œ animation์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Œ
// Variants
const boxVar = {
  start: {
    opacity: 0,
    scale: 0,
  },
  end: {
    opacity: 1,
    scale: 1,
    rotateZ: 360,
  },
  leaving: {
    opacity: 0,
    scale: 0,
    y: 100,
  },
};

<AnimatePresence>
  {isShow ? (
    <Box variants={boxVar} initial="start" animate="end" exit="leaving" />
  ) : null}
</AnimatePresence>;
  • custom: ๊ฐ animation ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•ด ๋™์  variants๋ฅผ ์ ์šฉ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ์†์„ฑ
// Variants
const boxVar = {
  invisible: (isBack: boolean) => {
    return {
      x: isBack ? -300 : 300,
      opacity: 0,
      scale: 0,
    };
  },
  visible: {
    x: 0,
    opacity: 1,
    scale: 1,
    transition: {
      duration: 0.3,
    },
  },
  exit: (isBack: boolean) => {
    return {
      x: isBack ? 300 : -300,
      opacity: 0,
      scale: 0,
      transition: {
        duration: 0.3,
      },
    };
  },
};

<AnimatePresence custom={back}>
  <Box
    custom={back}
    variants={boxVar}
    initial="invisible"
    animate="visible"
    exit="exit"
    key={visible}
  >
    {visible}
  </Box>
</AnimatePresence>;
  • isBack์ด true์™€ false์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ ์šฉ

Element์˜ key๋ฅผ ๋ฐ”๊ฟ”์ฃผ๋ฉด React๋Š” element๊ฐ€ ์‚ฌ๋ผ์กŒ๋‹ค๊ณ  ํŒ๋‹จํ•œ๋‹ค.

๐Ÿ“„ Layout

  • Element์— layout ์†์„ฑ์„ ๋ถ€์—ฌํ•˜๋ฉด ํ•ด๋‹น element์˜ css ์†์„ฑ์ด ๋ณ€๊ฒฝ ๋  ๋•Œ ๋งˆ๋‹ค animation์„ ๋ถ€์—ฌํ•œ๋‹ค.
  • CSS์˜ ๋ณ€ํ™”๊ฐ€ ์ž๋™์œผ๋กœ animate ๋˜๋Š” ๊ฒƒ
<Container onClick={toggleClicked}>
  <Box
    style={{
      justifyContent: clicked ? "center" : "flex-start",
      alignItems: clicked ? "center" : "flex-start",
    }}
  >
    <Circle layout />
  </Box>
</Container>
  • ๊ฐ element์— layoutId๋ฅผ ๋ถ€์—ฌํ•˜์—ฌ element๋ฅผ ์—ฐ๊ฒฐํ•˜๊ณ  ๋‘ element ์‚ฌ์ด์˜ css ๋ณ€ํ™”๋ฅผ animation์œผ๋กœ ์—ฐ๊ฒฐ์‹œ์ผœ์ค€๋‹ค
<Container onClick={toggleClicked}>
  <Box>{clicked && <Circle layoutId="circle" />} </Box>
  <Box>{!clicked && <Circle layoutId="circle" />}</Box>
</Container>

About

Practice framer-motion

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published