framer-motion

Hyemimi·2023년 1월 11일
0

React

목록 보기
17/17
post-custom-banner

install

npm install framer-motion

특이사항

  <div></div>
  <motion.div></motion.div>

<div /> 등과 같이 HTML 태그는 <motion.div /> 로 사용한다.

const Box = styled(motion.div)`
  height: 200px;
  background-color: rgba(255, 255, 255, 1);
  border-radius: 40px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
`;

styled-components로 사용하기 위해선 위와 같이 styled(motion.div)

Variants

const boxVariants = {
  start: { opacity: 0, scale: 0.5 },
  end: { scale: 1, opacity: 1, transition: { type: "spring" } },
};

위와 같은 variants를 생성해준 뒤,

<Box variants={boxVariants} initial="start" animate="end">
        <Circle />
        <Circle />
        <Circle />
        <Circle />
</Box>

<Box></Box> 컴포넌트에 주면, 자식 Circle 컴포넌트들에도 시작으로 start property 값, 끝으로 end property 값들을 주는 것과 다름 없다.

AnimationPresence

prop으로 initial animate exit

<AnimatePresence>
        {id ? (
          <Overlay
            onClick={() => setId(null)}
            initial={{ backgroundColor: "rgba(0, 0, 0, 0)" }}
            animate={{ backgroundColor: "rgba(0, 0, 0, 0.5)" }}
            exit={{ backgroundColor: "rgba(0, 0, 0, 0)" }}
          >
            <Box layoutId={id} style={{ width: 400, height: 200 }} />
          </Overlay>
        ) : null}
</AnimatePresence>

같은 layoutId를 가진 컴포넌트 사이의 연결을 도우며 "애니메이션"으로 보여준다.

연습 코드

import React, { useState } from "react";
import styled from "styled-components";
import { motion, AnimatePresence, useAnimation } from "framer-motion";

const Wrapper = styled.div`
  height: 100%;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
`;
const Grid = styled.div`
  display: grid;
  width: 50vw;
  gap: 10px;
  grid-template-columns: repeat(2, 1fr);
  &:button {
    display: flex;
  }
`;
const Box = styled(motion.div)`
  display: flex;
  align-items: center;
  justify-content: center;
  height: 200px;
  background-color: green;
  border-radius: 5px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);

  transform-origin: ${(props) =>
    props.hov === "hov"
      ? "bottom right"
      : props.hov === "no"
      ? "top left"
      : null};
`;

const Overlay = styled(motion.div)`
  width: 100%;
  height: 100%;

  position: absolute;
  display: flex;
  justify-content: center;
  align-items: center;
`;
const Circle = styled(motion.span)`
  width: 5rem;
  height: 5rem;
  background: black;
  position: absolute;
  border-radius: 50%;
  margin: 0 auto;
`;

const BoxVariants = {
  hover: { scale: 1.3, transition: { type: "tween" } },
  init: { scale: 1 }
};
const ButtonVariants = {
  active: {
    color: "red",
    scale: 1.3,
    transition: { type: "tween", duration: 0.5 }
  },
  disabled: { scale: 1 }
};

const Button = styled(motion.button)`
  height: 30px;
  width: 80px;
  border: 1px solid gray;
  background-color: transparent;
  border-radius: 5px;
  display: flex-end;
  margin: 30px;
  color: blue;
  cursor: pointer;
`;
const Column = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin: 30px;
`;

function App() {
  const [id, setId] = useState(null);
  const [show, setIsShown] = useState(true);
  const [active, setIsActive] = useState(false);
  const circleClick = () => {
    setIsActive(!active);
    setIsShown(!show);
  };

  return (
    <>
      <Wrapper>
        <Column>
          <Grid>
            {["1", "2", "3", "4"].map((n) =>
              n === "1" || n === "4" ? (
                <Box
                  hov={n === "1" ? "hov" : "no"}
                  variants={BoxVariants}
                  initial="init"
                  whileHover="hover"
                  onClick={() => setId(n)}
                  key={n}
                  layoutId={n}
                />
              ) : (
                <Box onClick={() => setId(n)} key={n} layoutId={n}>
                  {show && n === "2" ? (
                    <Circle
                      layoutId="circle"
                      animate={{ transition: { duration: 1 } }}
                    />
                  ) : null}
                  {!show && n === "3" ? (
                    <Circle
                      layoutId="circle"
                      animate={{ transition: { duration: 1 } }}
                    />
                  ) : null}
                </Box>
              )
            )}
          </Grid>
          <Button
            variants={ButtonVariants}
            animate={active ? "active" : "disabled"}
            onClick={circleClick}
          >
            SWITCH
          </Button>
        </Column>

        <AnimatePresence>
          {id ? (
            <Overlay
              onClick={() => setId(null)}
              initial={{ backgroundColor: "rgba(0, 0, 0, 0)" }}
              animate={{ backgroundColor: "rgba(0, 0, 0, 0.5)" }}
              exit={{ backgroundColor: "rgba(0, 0, 0, 0)" }}
            >
              <Box layoutId={id} style={{ width: 300, height: 200 }} />
            </Overlay>
          ) : null}
        </AnimatePresence>
      </Wrapper>
    </>
  );
}

export default App;

profile
암냠냠
post-custom-banner

0개의 댓글