참고 유튜브
메인페이지와 서브페이지의 이미지 위치를 동일하게 놔야 부드럽게 움직이는 것처럼 보인다.
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 }}>