[MainProject] 메인페이지 구성
메인 페이지 구성
웹페이지의 메인 페이지가 되는 페이지를 구현했다.
구성
- 5개의 섹션으로 나눠진 페이지
- 스크롤을 통한 섹션 이동
- 사이드바 섹션 네비게이션을 통한 이동
- 각각 웹페이지를 소개해주는 페이지로 구성
섹션 1: 캐러셀을 통한 페이지 소개
섹션 2: 애니매이션을 통한 제품 소개
섹션 3: 애니매이션을 통한 제품 소개
섹션 4: 애니매이션을 통한 주문 방법 소개
섹션 5: 커스텀 제품 시안 리뷰들
메인 페이지 (Main.tsx)
전체 코드
//Main.tsx
import { useState, useEffect } from "react";
import styled from "styled-components";
import MainSection1 from "../../components/Main/MainSection1";
import MainSection2 from "../../components/Main/MainSection2";
import MainSection3 from "../../components/Main/MainSection3";
import MainSection4 from "../../components/Main/MainSection4";
import MainSection5 from "../../components/Main/MainSection5";
import MainSidebar from "../../components/Main/MainSidebar";
import ModalPortal from "../../share/ModalPortal";
import { throttle } from "../../utils/Throttle";
const MainContainer = styled.div`
height: 100vh;
overflow: hidden;
`;
const AnimatedSection = styled.div`
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 500ms ease-in-out;
pointer-events: none;
&.active {
opacity: 1;
pointer-events: auto;
}
`;
function Main() {
const [activeSection, setActiveSection] = useState(1);
const handleScrollToSection = (section: number) => {
setActiveSection(section);
};
useEffect(() => {
const handleWheel = throttle((event: WheelEvent) => {
event.preventDefault();
const scrollPosition = window.scrollY;
if (event.deltaY < 0 && activeSection > 1) {
setActiveSection(activeSection - 1);
} else if (event.deltaY > 0 && activeSection < 5) {
setActiveSection(activeSection + 1);
}
setTimeout(() => {
window.scrollTo({ top: scrollPosition });
}, 0);
}, 1000);
window.addEventListener("wheel", handleWheel, { passive: false });
return () => {
window.removeEventListener("wheel", handleWheel);
};
}, [activeSection]);
useEffect(() => {
const handleScroll = () => {
const sections = Array.from(document.querySelectorAll(".section"));
const sectionOffsets = sections.map(
(section) => section.getBoundingClientRect().top + window.pageYOffset
);
const scrollPosition = window.scrollY + window.innerHeight / 2;
const activeIndex = sectionOffsets.findIndex(
(offset) => offset > scrollPosition
);
if (activeIndex !== -1) {
setActiveSection(activeIndex + 1);
}
};
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
return (
<MainContainer>
<ModalPortal>
<MainSidebar
activeSection={activeSection}
handleScrollToSection={handleScrollToSection}
/>
</ModalPortal>
<AnimatedSection className={activeSection === 1 ? "active" : ""}>
<MainSection1 id={"section1"} />
</AnimatedSection>
<AnimatedSection className={activeSection === 2 ? "active" : ""}>
<MainSection2 id={"section2"} isActive={activeSection === 2} />
</AnimatedSection>
<AnimatedSection className={activeSection === 3 ? "active" : ""}>
<MainSection3 id={"section3"} isActive={activeSection === 3} />
</AnimatedSection>
<AnimatedSection className={activeSection === 4 ? "active" : ""}>
<MainSection4 id={"section4"} isActive={activeSection === 4} />
</AnimatedSection>
<AnimatedSection className={activeSection === 5 ? "active" : ""}>
<MainSection5 id={"section5"} />
</AnimatedSection>
</MainContainer>
);
}
export default Main;
Main 컴포넌트 정의와 상태 설정
function Main() {
const [activeSection, setActiveSection] = useState(1);
activeSection이라는 상태 변수를 사용하여 현재 활성화된 섹션을 추적
handleScrollToSection 함수
const handleScrollToSection = (section: number) => {
setActiveSection(section);
};
handleScrollToSection 함수는 MainSidebar 컴포넌트에서 사용되며, 클릭한 섹션으로 스크롤을 이동시키는 역할
스크롤 이벤트 핸들링을 통한 섹션 변경
useEffect(() => {
const handleWheel = throttle((event: WheelEvent) => {
event.preventDefault();
const scrollPosition = window.scrollY;
if (event.deltaY < 0 && activeSection > 1) {
setActiveSection(activeSection - 1);
} else if (event.deltaY > 0 && activeSection < 5) {
setActiveSection(activeSection + 1);
}
setTimeout(() => {
window.scrollTo({ top: scrollPosition });
}, 0);
}, 1000);
window.addEventListener("wheel", handleWheel, { passive: false });
return () => {
window.removeEventListener("wheel", handleWheel);
};
}, [activeSection]);
스크롤 이벤트를 감지하고, 스크롤 방향에 따라 활성 섹션을 변경하는 역할 throttle
함수는 스크롤 이벤트를 일정 간격으로 제어하여 부하를 줄이는 역할
스크롤 이벤트 핸들링을 통한 현재 활성 섹션 변경
useEffect(() => {
const handleScroll = () => {
const sections = Array.from(document.querySelectorAll(".section"));
const sectionOffsets = sections.map(
(section) => section.getBoundingClientRect().top + window.pageYOffset
);
const scrollPosition = window.scrollY + window.innerHeight / 2;
const activeIndex = sectionOffsets.findIndex(
(offset) => offset > scrollPosition
);
if (activeIndex !== -1) {
setActiveSection(activeIndex + 1);
}
};
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
스크롤 이벤트를 감지하여 현재 화면에 표시되는 섹션의 인덱스를 추적하고, 해당 섹션을 활성화시키는 역할
사이드바
MainSidebar 컴포넌트 정의 및 상태 설정
interface MainSidebarProps {
activeSection: number;
handleScrollToSection: (section: number) => void;
}
const MainSidebar: React.FC<MainSidebarProps> = ({ activeSection, handleScrollToSection }) => {
const [clickedSection, setClickedSection] = useState(0);
const handleClick = (section: number) => {
setClickedSection(section);
handleScrollToSection(section);
setTimeout(() => setClickedSection(0), 300);
};
handleClick
함수는 섹션 아이템 클릭 시 호출되는 함수
- 해당 섹션을 활성화하고 스크롤을 해당 섹션으로 이동한다.
- 클릭된 섹션은 스크롤 후 일정 시간 후에 원래 상태로 돌아오도록 설정한다.
사이드바 아이템 렌더링
return (
<SidebarContainer>
<SidebarItem
active={activeSection === 1}
clicked={clickedSection === 1}
onClick={() => handleClick(1)}
data-label="BUYTE"
/>
<SidebarItem
active={activeSection === 2}
clicked={clickedSection === 2}
onClick={() => handleClick(2)}
data-label="BUYTE의 제품"
/>
{/* 나머지 섹션 아이템들 */}
</SidebarContainer>
);
- 사이드바 아이템은 섹션 개수에 따라 반복하여 렌더링된다.
- 각 아이템에는 해당 섹션의 활성 여부와 클릭 여부를 전달하여 스타일을 조정하고, 클릭 이벤트를 설정한다.
- 마우스 오버 시 섹션 이름이 나타나고, 클릭 시 클릭 효과와 함께 활성 섹션이 변경된다.
결과
메인 페이지
댓글남기기