react framer-motion 누르면 커지는 이미지

해적왕·2022년 7월 17일
0
post-thumbnail
post-custom-banner

참고 유튜브

https://www.youtube.com/watch?v=3QrkCmsfewM&t=1702s

메인페이지와 서브페이지의 이미지 위치를 동일하게 놔야 부드럽게 움직이는 것처럼 보인다.

app.js

import { Routes, Route, useLocation } from 'react-router-dom';
import Main from './components/Main';
import Sub from './components/Sub';
import { AnimatePresence } from 'framer-motion';

function App() {
  const location = useLocation();

  const imageDetails = {
    width: 424,
    height: 550,
  };

  return (
    <>
       <AnimatePresence initial={false} exitBeforeEnter>
        <Routes location={location} key={location.pathname}>
          <Route path="/" element={<Main imageDetails={imageDetails} />} />
          <Route path='/:id' element={<Sub imageDetails={imageDetails} />}/>   
        </Routes>
      </AnimatePresence>
    </>
  );
}

export default App;

AnimatePresence 사용하는 이유는 페이지를 나갈 때도 효과를 주기 위해서다. key값은 필수이다.

Main.js

import styled from "styled-components";
import { Link } from "react-router-dom";
import { motion } from "framer-motion";
import { useEffect, useState } from "react";

const transition = { duration: 0.6, ease: [0.43, 0.13, 0.23, 0.96] };

const Main = ({ imageDetails }) => {
  return (
    <Container>
    <Contain>
      <ImgWrap>
        <Img
          style={{
            width: imageDetails.width,
            height: imageDetails.height,
          }}>
          <Link to={`/plants`}>
            <motion.img
              whileHover={{ scale: 1.1 }}
              src=""
              transition={transition}
              exit={{ scale: 1.0 }}
            />
          </Link>
        </Img>
      </ImgWrap>
    </Contain>
    </Container>
  );
};
export default Main;

const Container = styled.div`
  width:100%;
  height: auto;
`

const Contain = styled.div`
  width:100%;
  height:100vh;
`

const ImgWrap = styled.div`
  width:100%;
  height:80vh;
  display:flex;
  align-items:end;
  justify-content:center;
`

const Img = styled.div`
  overflow:hidden;
  img{
    width: 100%;
  }
`

const Text = styled.div`
  height:20vh;
  margin:0 auto;
`

처음엔 페이지가 나가면서 이미지를 커지게 했더니 Sub.js에서 묘하게 버벅였고 이미지를 동일한 곳에 놓기 어려웠다. 여기서의 시행착오로 시간이 많이 들었다.

마우스 오버 시 확대와 exit가 되면 다시 원래 scale이 돌아올 수 있게 했다.

Sub.js

import { useEffect, useState } from "react";
import { motion, useScroll, useTransform } from "framer-motion";
import styled from "styled-components";

const transition = { duration: 0.5, ease: [0.6, 0.01, 0.3, 0.9] };

const Sub = ({ imageDetails }) => {

    const [canScroll, setCanScroll] = useState(false);

    useEffect(() => {
        if (canScroll === false) {
            document.querySelector("body").classList.add("no-scroll");
        } else {
            document.querySelector("body").classList.remove("no-scroll");
        }
    }, [canScroll]);

    return (
        <Container
            onAnimationComplete={() => setCanScroll(true)}
            initial='initial'
            animate='animate'
            exit='exit'
        >
            <Contain>
                <Text>
                    plants
                </Text>
                <ImgWrap
                    initial={{
                        y: 0,
                    }}
                    animate={{
                        y: '32%',
                    }}
                    transition={transition}
                >
                    <Img
                        style={{
                            width: imageDetails.width,
                            height: imageDetails.height,
                            scale: scale,
                        }}
                        animate={{
                            width: "100%",
                        }}
                        transition={{ ...transition, delay: 0.5 }}
                    >
                        <motion.img
                            src="https://blog.kakaocdn.net/dn/SM81V/btrHqQXbaRp/U0q3HKRKS6Nk5zUSDOsObk/img.jpg"
                        />
                    </Img>
                </ImgWrap>
            </Contain>
        </Container>
    )
}
export default Sub;

const Container = styled(motion.div)`
   width:100%;
   height:auto;
   display:flex;
   flex-direction:column;
`

const Contain = styled.div`
  height:100vh;
`

const ImgWrap = styled(motion.div)`
  width:100%;
  height:80vh;
  display:flex;
  align-items:first;
  justify-content:center;
  overflow:hidden;
`

const Img = styled(motion.div)`
  overflow:hidden;
  img{
    width: 100%;
  }
`

const Text = styled(motion.div)`
  height:20vh;
  margin:0 auto;
`

canScroll은 이미지가 내려가면서 스크롤이 줄었다 늘었다가 할 때 잠시동안 안보이게 해주기 위함이다.

index.css 나 app.css 에 넣어준다.

.no-scroll {
    overflow-y: hidden;
  }

이제 코드를 살펴보면
처음에 이미지가 사이즈 그대로 내려 가게 하기 위해서

    <ImgWrap
      initial={{y: 0}}
      animate={{y: '32%'}}
      transition={transition}
     >

initial은 처음의 라는 뜻이다. 처음은 0이었다가 32%로 아래로 내려오게 된다.

 <Img
    style={{
      width: imageDetails.width,
      height: imageDetails.height,
      }}
      animate={{ width: "100%" }}
      transition={{ ...transition, delay: 0.5 }}
     >

처음 사이즈는 고정하고 100%로 넓어진다. delay를 줘서 위 ImgWrap보다 나중에 적용되게 했다. 그럼 끝이다.

나중에 버벅거리지 않으려면 처음 레이아웃을 짤 때 정확하게 나눠서 짜야 고생을 안 할 것 같다!

뻘짓을 오래 했더니 이미지 위치 옮겨지는 원리는 확실히 깨달았다.


스크롤 시 커지는 이미지

import { motion, useScroll, useTransform } from "framer-motion";

const Sub = ({ imageDetails }) => {
    const { scrollYProgress } = useScroll();
    const scale = useTransform(scrollYProgress, [0, 1], [1, 1.15]);

    return ()

그리고 scale은 Img안에 넣어주면 된다. 그럼 스크롤시 확대가 되는 것을 볼 수 있다.
<Img style={{ scale: scale }}>

profile
프론트엔드
post-custom-banner

0개의 댓글