Skip to content

๐ŸŽข framer-motion ์• ๋‹ˆ๋ฉ”์ด์…˜ ์—ฐ์Šต

Notifications You must be signed in to change notification settings

rigood/framer-motion

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

10 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐ŸŽข Framer Motion ์• ๋‹ˆ๋ฉ”์ด์…˜ ์—ฐ์Šต


1. Box์™€ Modal์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์—ฐ๊ฒฐํ•˜๊ธฐ

  • layoutId๋ฅผ ์ด์šฉํ•˜์—ฌ Box์™€ Modal์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • whileHover๋ฅผ ํ†ตํ•ด hover ์‹œ ์Šคํƒ€์ผ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<Box
  onClick={() => setBoxId("first")}
  layoutId={"first"}
  whileHover={{
    scale: 1.1,
    transition: { duration: 0.3 },
  }}
  ref={boxRef}
>
  Hover me! <br /> Click me!
</Box>

{boxId && (
  <Overlay
      onClick={() => setBoxId(null)}
      initial={{ backgroundColor: "rgba(0,0,0,0)" }}
      animate={{ backgroundColor: "rgba(0,0,0,0.5)" }}
      exit={{ backgroundColor: "rgba(0,0,0,0)" }}
  >
      <Modal layoutId={boxId} $boxWidth={getBoxWidth()!}>
         Thank you ๐Ÿฅฐ
      </Modal>
  </Overlay>
)}

2. ๋ฐ˜์‘ํ˜• ๋ชจ๋‹ฌ

  • useRef๋ฅผ ํ†ตํ•ด ์–ป์€ Box์˜ offsetWidth ๊ฐ’์„ styled-components์˜ props๋กœ ์ „๋‹ฌํ•˜์—ฌ ์ฐฝ ํฌ๊ธฐ์— ๋”ฐ๋ผ ๋ชจ๋‹ฌ์˜ width๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
Full Screen Responsive
const boxRef = useRef < HTMLDivElement > null;
const getBoxWidth = () => {
  if (boxRef.current) {
    return boxRef.current.offsetWidth;
  }
};
const Modal = styled(motion.div)<{ $boxWidth: number }>`
  width: ${({ $boxWidth }) => $boxWidth + "px"};
  // ...
`;

3. ๊ณต ์˜ฎ๊ธฐ๊ธฐ

  • layoutId๋ฅผ ์ด์šฉํ•˜์—ฌ Circle์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ switch ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const [circleSwitched, setCircleSwitched] = useState(false);
const switchCircle = () => setCircleSwitched((prev) => !prev);

<Box>{!circleSwitched && <Circle layoutId="switch" />}</Box>
<Box>{circleSwitched && <Circle layoutId="switch" />}</Box>

4. ์Šคํฌ๋กค ์• ๋‹ˆ๋ฉ”์ด์…˜

  • framer-motion์˜ useAnimation ํ›…๊ณผ react-intersection-observer์˜ useInView ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํฌ๋กค ์‹œ FadeIn ๋˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

// ์ปค์Šคํ…€ ํ›…
const useScrollAnimation = () => {
  const animation = useAnimation();
  const { ref, inView } = useInView();

  useEffect(() => {
    if (inView) {
      // ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ๋ทฐํฌํŠธ ๋‚ด์— ๋“ค์–ด์˜ค๋ฉด visible ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹คํ–‰
      animation.start("visible");
    } else {
      // ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ๋ทฐํฌํŠธ์—์„œ ๋ฒ—์–ด๋‚˜๋ฉด hidden ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹คํ–‰
      animation.start("hidden");
    }
  }, [animation, inView]);

  return { ref, animation };
};

// ์ปค์Šคํ…€ ํ›…์ด ์ ์šฉ๋œ Wrapper ์ปดํฌ๋„ŒํŠธ
const ScrollWrapper = ({ children }: ScrollWrapperProps) => {
  const { ref, animation } = useScrollAnimation();

  return (
    <motion.div
      ref={ref}
      variants={fadeInVariants}
      initial="hidden"
      animate={animation}
    >
      {children}
    </motion.div>
  );
};

// ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ
<ScrollWrapper>
  <ExampleComponent />
</ScrollWrapper>;

About

๐ŸŽข framer-motion ์• ๋‹ˆ๋ฉ”์ด์…˜ ์—ฐ์Šต

Topics

Resources

Stars

Watchers

Forks