프로젝트 때 사이트에 무한스크롤 기능을 넣었더니 사이트 최상단으로 스크롤하는 버튼이 필요해졌다.
내가 원하는 최상단 스크롤 버튼의 기능은 다음과 같았다.
- Framer-motion 라이브러리를 써서 나타나고 사라질 때 애니메이션이 적용되게 할 것
- 스크롤을 아래로 내릴 때 버튼이 나타나고, 위로 올리면 사라질 것
- 메인 페이지에서 스크롤을 내리지 않았을 때는 노출되지 않을 것
- 버튼을 누르면 부드럽게 스크롤
효율이 좋다고는 할 수는 없겠지만 어쨌든 구현에는 성공했다.
// import 및 필요하지 않은 부분은 생략함
const Home = () => {
const [btnView, setBtnView] = useState(false);
const scrollRef = useRef(0);
useEffect(() => {
const timer = setInterval(() => {
window.addEventListener("scroll", handleScroll);
}, 100);
return () => {
clearInterval(timer);
window.removeEventListener("scroll", handleScroll);
};
}, []);
...
const handleScroll = () => {
if (window.scrollY > scrollRef.current) {
setBtnView(true);
} else {
setBtnView(false);
}
scrollRef.current = window.scrollY;
};
const scrollToTop = () => {
window.scrollTo({
top: 0,
behavior: "smooth",
});
};
...
return (
<HomeWrapper>
...
<AnimatePresence>
{btnView ? (
<ScrollToTopBtn
initial={{ opacity: 0, y: 50 }}
animate={{
opacity: 1,
y: 0,
}}
exit={{ opacity: 0, y: 50 }}
onClick={scrollToTop}
>
</ScrollToTopBtn>
) : null}
</AnimatePresence>
</HomeWrapper>
)
}
우선 useEffect
안에서 타이머를 등록해준다. 이 과정을 통해 특정 시간마다 이벤트를 감지할 수 있게 된다.
그리고 위처럼 작성한 handleScroll()
함수 안에서 window.scrollY
를 console.log
로 출력해 보면
이렇게 스크롤 위치가 출력되는 것을 확인할 수 있다.
나는 scrollRef
를 설정해 두고, scrollRef
와 현재 window.scrollY
를 비교해서 스크롤이 내려갔다고 판단되면 btnView
를 true
로 설정해주었다.
그런 다음 scrollRef
를 업데이트한다.
scrollToTop()
은 버튼을 눌렀을 때 최상단 스크롤을 실행해준다.
behavior: "smooth"
속성을 주면 부드럽게 스크롤된다.
지금 코드는 Framer-motion 라이브러리를 써서 애니메이션을 적용했기 때문에 return
쪽 코드가 조금 복잡한데, 단순히 노출만 하는 정도라면
return (
<HomeWrapper>
...
{btnView ? (
<ScrollToTopBtn onClick={scrollToTop}></ScrollToTopBtn>
) : null}
</HomeWrapper>
)
정도로 충분할 것 같다.
무한스크롤 때문에 스크롤바만 보면 조금 어색할 수 있지만, 스크롤 버튼은 정상적으로 작동하고 있다.