
포트폴리오를 만들기 위해 레퍼런스를 둘러보고 있는데, 하나같이 스크롤을 내리면 섹션이 바뀌는 구조였다. 그래서 구글에 쳐보니 fullpage라고 한다고 한다.
좀 더 찾아보니 fullpage.js라는 라이브러리가 있긴 한데 제이쿼리라서 일단 패쓰..
react-full-page라는 라이브러리가 있어서 적용을 해 봤는데 로컬에서는 잘 나오는데 vercel로 배포를 하면 아무것도 안 뜨는 이슈가 발생했다.
게다가 레퍼런스 자료가 너어어무 없었다..
오히려 커스텀 한 레퍼런스 자료가 많을 정도로 !
그래서 그냥 수작업으로 만들기로 결정 !!
1단계 : 기본코드와 CSS 구성
2단계 : 마우스 휠 이벤트 함수 구성
body {
margin: 0;
overflow-y: hidden;
}
.outer {
height: 100vh;
overflow-y: auto;
}
/* 화면에서 스크롤바 안보이게 */
.outer::-webkit-scrollbar {
display: none;
}
.inner {
}
.bg-yellow {
background-color: #f7f6cf;
}
.bg-blue {
background-color: #b6d8f2;
}
.bg-pink {
background-color: #f4cfdf;
}
가상 클래스를 설정해 display 속성을 none으로 설정해 스크롤이 동작은 하지만 눈에는 안 보이게 구현
import { useRef } from "react";
import "./App.css";
function App() {
const outerDivRef = useRef();
return (
<div ref={outerDivRef} className="outer">
<div className="inner bg-yellow">1</div>
<div className="inner bg-blue">2</div>
<div className="inner bg-pink">3</div>
</div>
);
}
export default App;
import { useEffect, useRef } from "react";
import "./App.css";
function App() {
const outerDivRef = useRef();
useEffect(() => {
const wheelHandler = (e) => {
e.preventDefault();
// 스크롤 행동 함수
};
const outerDivRefCurrent = outerDivRef.current;
outerDivRefCurrent.addEventListener("wheel", wheelHandler);
return () => {
outerDivRefCurrent.removeEventListener("wheel", wheelHandler);
};
}, []);
return (
<div ref={outerDivRef} className="outer">
<div className="inner bg-yellow">1</div>
<div className="inner bg-blue">2</div>
<div className="inner bg-pink">3</div>
</div>
);
}
export default App;
outer에 스크롤이 걸려있고, 그 outer (classNmae =outer)에 wheel 이벤트 핸들러를 추가
휠 이벤트는 마우스의 휠을 움직일 때마다 발생.
그니까 outer의 outerDivRef가 outerDivRefCurrent와 연결돼 있어서, outerDivRefCurrent가 실행된다.
wheelHandler 함수
event 객체를 파라미터로 받고, 필요한 로직은 두 개다.
마우스 휠이 움직인 방향 같은 경우 event 객체 내부에 있는 deltaY라는 값으로 구할 수 있다.
delta Y가 양수면 마우스 휠을 아래로 굴린거고, 다음페이지로 넘어가는 것.
delta Y가 음수면 마우스 휠을 위로 굴린거고, 이전페이지로 돌아가는 것.
현재 페이지는 outerDiv의 scrollTOp과 window 객체의 innerHeight 값을 활용해 wheel 이벤트 발생 시점의 현재페이지를 확인할 수 있다.
scrollTop은 스크롤바 막대 위쪽의 위치이고 innerHeight은 브라우저 화면의 세로길이
wheel 함수의 로직은 다음과 같다
const wheelHandler = (e) => {
e.preventDefault();
const { deltaY } = e;
const { scrollTop } = outerDivRef.current; // 스크롤 위쪽 끝부분 위치
const pageHeight = window.innerHeight; // 화면 세로길이, 100vh와 같습니다.
if (deltaY > 0) {
// 스크롤 내릴 때
if (현재 1페이지면) {
// 2페이지로 이동
} else if (현재 2페이지면) {
// 3페이지로 이동
} else (현재 3페이지면) {
// 3페이지로 이동
}
} else {
// 스크롤 올릴 때
if (현재 1페이지면) {
// 1페이지로 이동
} else if (현재 2페이지면) {
// 1페이지로 이동
} else (현재 3페이지면) {
// 2페이지로 이동
}
}
};
정리를 해보자
const wheelHandler = (e) => {
e.preventDefault();
const { deltaY } = e;
const { scrollTop } = outerDivRef.current; // 스크롤 위쪽 끝부분 위치
const pageHeight = window.innerHeight; // 화면 세로길이, 100vh와 같습니다.
if (deltaY > 0) {
// 스크롤 내릴 때
if (scrollTop >= 0 && scrollTop < pageHeight) {
//현재 1페이지
console.log("현재 1페이지, down");
outerDivRef.current.scrollTo({
top: pageHeight,
left: 0,
behavior: "smooth",
});
} else if (scrollTop >= pageHeight && scrollTop < pageHeight * 2) {
//현재 2페이지
console.log("현재 2페이지, down");
outerDivRef.current.scrollTo({
top: pageHeight * 2,
left: 0,
behavior: "smooth",
});
} else {
// 현재 3페이지
console.log("현재 3페이지, down");
outerDivRef.current.scrollTo({
top: pageHeight * 2,
left: 0,
behavior: "smooth",
});
}
} else {
// 스크롤 올릴 때
if (scrollTop >= 0 && scrollTop < pageHeight) {
//현재 1페이지
console.log("현재 1페이지, up");
outerDivRef.current.scrollTo({
top: 0,
left: 0,
behavior: "smooth",
});
} else if (scrollTop >= pageHeight && scrollTop < pageHeight * 2) {
//현재 2페이지
console.log("현재 2페이지, up");
outerDivRef.current.scrollTo({
top: 0,
left: 0,
behavior: "smooth",
});
} else {
// 현재 3페이지
console.log("현재 3페이지, up");
outerDivRef.current.scrollTo({
top: pageHeight,
left: 0,
behavior: "smooth",
});
}
}
};
우측에 점을 3개 만들어 현재 페이지 순서의 점만 칠하는 로직 추가하기
//Dots.js
const Dot = ({ num, currentPage }) => {
return (
<div
style={{
width: 10,
height: 10,
border: "1px solid black",
borderRadius: 999,
backgroundColor: currentPage === num ? "black" : "transparent",
transitionDuration: 1000,
transition: "background-color 0.5s",
}}
></div>
);
};
const Dots = ({ currentPage }) => {
return (
<div style={{ position: "fixed", top: "50%", right: 100 }}>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
alignItems: "center",
width: 20,
height: 100,
}}
>
<Dot num={1} currentPage={currentPage}></Dot>
<Dot num={2} currentPage={currentPage}></Dot>
<Dot num={3} currentPage={currentPage}></Dot>
</div>
</div>
);
};
export default Dots;
currentPage를 인자로 받고 dot에 num과 currentPage를 넘겨 현재 페이지와 숫자가 일치한다면 색깔을 다르게 삼항연산자로 스타일을 적용
//App.tsx
const [currentPage, setCurrentPage] = useState(1);
return (
<div ref={outerDivRef} className="outer">
<Dots currentPage={currentPage} />
<div className="inner bg-yellow">1</div>
<div className="divider"></div>
<div className="inner bg-blue">2</div>
<div className="divider"></div>
<div className="inner bg-pink">3</div>
</div>
);
}