AnimatePresence 컴포넌트와 end
속성을 사용하면 컴포넌트가 언마운트 하기 전에 모션을 처리할 수 있어 페이지 라우트(route) 전환 시 모션을 적용할 수 있습니다.
그러기 위해선 경로의 전체 설정을 AnimatePresence
컴포넌트로 래핑해야 합니다.
import { AnimatePresence }from 'framer-motion';
function App() {
return (
<AnimatePresence>
<Switch>
<Route path="/" exact component={HomePage} />
<Route path="/movies" component={MovieListPage} />
<Route path="/movie/:id" component={MovieDetailPage} />
<RouteGuard path="/bookmarks" component={BookmarkPage} />
<Route path="/page-not-found" component={PageNotFound} />
<Redirect to="/page-not-found" />
</Switch>
</AnimatePresence>
)
}
이 설정만으로 경로 모션이 작동하지는 않습니다. 페이지가 언제 전환될 지 모르기 때문입니다. 그러므로 페이지가 전환 됨을 알 수 있도록 (location.pathname
변경) React Router로부터 useLocation
훅을 가져와 사용해야 합니다.
import { AnimatePresence }from 'framer-motion';
import { Switch, Route, Redirect, useLocation }from 'react-router-dom';
useLocation
훅으로부터 현재 페이지 경로 정보를 가져와 Switch 컴포넌트의 location
속성에 연결해 페이지가 언제 변경(언마운트) 되는 지 알려줘야 합니다. 그리고 key
속성 값으로 location.key
를 설정해 React에 의해 재조정 될 수 있도록 설정합니다.
functionApp() {
const location = useLocation();
return (
{/*
exitBeforeEnter 속성을 설정하면 이전 페이지 모션이 완료된 이후,
다음 페이지 모션이 시작 됨 (참고: <https://bit.ly/2R14lHt>)
*/}
<AnimatePresence exitBeforeEnter>
<Switch location={location} key={location.key}>
<Route path="/" exact component={HomePage} />
<Route path="/movies" component={MovieListPage} />
<Route path="/movie/:id" component={MovieDetailPage} />
<RouteGuard path="/bookmarks" component={BookmarkPage} />
<Route path="/page-not-found" component={PageNotFound} />
<Redirect to="/page-not-found" />
</Switch>
</AnimatePresence>
);
}
이제 각 페이지 컴포넌트가 진입할 때와 나갈 때 모션을 정해주면 페이지 전환 간 모션이 적용됩니다.
pageVariants
설정을 모든 페이지에서 불러와 적용하면 페이지에 일관된 페이지 전환 모션을 설정할 수 있습니다. 다른 방법은 MotionPage 컴포넌트를 만들어 모든 페이지의 컨테이너로 사용하면 일괄적으로 페이지 전환 모션을 손쉽게 제어가 가능합니다.
import { motion }from 'framer-motion';
// 페이지 배리에이션
export const pageVariants = {
hidden: { opacity: 0 },
visible: { opacity: 1, transition: { delay: 0.5, duration: 0.5 } },
slideOut: {
x: '-100vw',
transition: { ease: 'easeOut' }
}
};
export default function HomePage({ history }) {
return (
{/* 페이지를 모션 요소로 만든 후, 배리에이션 상태 이름을 설정 */}
<motion.div
className="home-page"
variants={pageVariants}
initial="hidden"
animate="visible"
exit="slideOut"
>
<Helmet>
<title>홈 ← "나의 영화" 서비스</title>
</Helmet>
<Effects message="ENTER" className={effect} />
<a
href="#go-to-movies"
onClick={(e) => {
e.preventDefault()
history.push('/movies')
}}
>
<img className={vision} src={image} alt="영화 목록 페이지로 이동" />
</a>
</motion.div>
);
}