279 lines
10 KiB
JavaScript
279 lines
10 KiB
JavaScript
/**
|
|
* intro-section.js
|
|
* 섹션 전환 및 스크롤 처리 로직
|
|
* 공통 모듈 활용 (ErrorHandler, DOMUtils, AnimationUtils, Utils)
|
|
*/
|
|
|
|
// 전역 의존성 (폴백 포함)
|
|
const _sectionDomUtils = typeof DOMUtils !== 'undefined' ? DOMUtils : null;
|
|
const _sectionErrorHandler = typeof ErrorHandler !== 'undefined' ? ErrorHandler : null;
|
|
const _sectionUtils = typeof Utils !== 'undefined' ? Utils : null;
|
|
const _sectionAnimationUtils = typeof AnimationUtils !== 'undefined' ? AnimationUtils : null;
|
|
|
|
/**
|
|
* 에러 처리 헬퍼
|
|
* @private
|
|
*/
|
|
function _handleError(error, context, additionalInfo = {}) {
|
|
if (_sectionErrorHandler) {
|
|
_sectionErrorHandler.handle(error, {
|
|
context: `IntroSection.${context}`,
|
|
component: 'IntroSection',
|
|
...additionalInfo
|
|
}, false);
|
|
} else {
|
|
console.error(`[IntroSection] ${context}:`, error, additionalInfo);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 특정 섹션으로 이동
|
|
* @param {number} index - 이동할 섹션 인덱스 (0, 1, 2)
|
|
*/
|
|
function goToSection(index) {
|
|
try {
|
|
// 입력 검증
|
|
if (typeof index !== 'number' || index < 0 || index > 2) {
|
|
_handleError(new Error(`유효하지 않은 섹션 인덱스: ${index}`), 'goToSection');
|
|
return;
|
|
}
|
|
|
|
if (typeof INTRO_STATE === 'undefined') {
|
|
_handleError(new Error('INTRO_STATE가 정의되지 않았습니다'), 'goToSection');
|
|
return;
|
|
}
|
|
|
|
if (INTRO_STATE.isAnimating) return;
|
|
INTRO_STATE.isAnimating = true;
|
|
|
|
// 자동 스크롤 타이머 정지
|
|
clearTimeout(INTRO_STATE.autoScrollTimer);
|
|
|
|
// DOM 요소 선택
|
|
const section1Text = _sectionDomUtils?.$("#section1Text") || document.getElementById("section1Text");
|
|
const section2Text = _sectionDomUtils?.$("#section2Text") || document.getElementById("section2Text");
|
|
const section3Text = _sectionDomUtils?.$("#section3Text") || document.getElementById("section3Text");
|
|
const cardsContainer = _sectionDomUtils?.$("#cardsContainer") || document.getElementById("cardsContainer");
|
|
const ctaBtn = _sectionDomUtils?.$("#ctaBtn") || document.getElementById("ctaBtn");
|
|
const scrollIndicator = _sectionDomUtils?.$("#scrollIndicator") || document.getElementById("scrollIndicator");
|
|
|
|
// 요소 존재 확인
|
|
if (!section1Text || !section2Text || !section3Text || !cardsContainer || !ctaBtn || !scrollIndicator) {
|
|
_handleError(new Error('필수 DOM 요소를 찾을 수 없습니다'), 'goToSection');
|
|
INTRO_STATE.isAnimating = false;
|
|
return;
|
|
}
|
|
|
|
// 현재 섹션 페이드 아웃
|
|
const currentSection = INTRO_STATE.currentSection;
|
|
if (currentSection === 0) {
|
|
if (_sectionDomUtils && _sectionDomUtils.addClasses) {
|
|
_sectionDomUtils.addClasses(section1Text, 'fade-out');
|
|
} else {
|
|
section1Text.classList.add("fade-out");
|
|
}
|
|
} else if (currentSection === 1) {
|
|
if (_sectionDomUtils && _sectionDomUtils.addClasses) {
|
|
_sectionDomUtils.addClasses(section2Text, 'fade-out');
|
|
} else {
|
|
section2Text.classList.add("fade-out");
|
|
}
|
|
|
|
// 카드 숨기기
|
|
["card1", "card2", "card3", "card4"].forEach((id) => {
|
|
const card = _sectionDomUtils?.$(`#${id}`) || document.getElementById(id);
|
|
if (card) {
|
|
if (_sectionDomUtils && _sectionDomUtils.removeClasses) {
|
|
_sectionDomUtils.removeClasses(card, 'show');
|
|
} else {
|
|
card.classList.remove("show");
|
|
}
|
|
}
|
|
});
|
|
} else if (currentSection === 2) {
|
|
if (_sectionDomUtils && _sectionDomUtils.addClasses) {
|
|
_sectionDomUtils.addClasses(section3Text, 'fade-out');
|
|
} else {
|
|
section3Text.classList.add("fade-out");
|
|
}
|
|
if (_sectionDomUtils && _sectionDomUtils.removeClasses) {
|
|
_sectionDomUtils.removeClasses(ctaBtn, 'show');
|
|
} else {
|
|
ctaBtn.classList.remove("show");
|
|
}
|
|
}
|
|
|
|
// 전환 애니메이션 지연
|
|
const transitionDelay = 400;
|
|
const delayFn = _sectionUtils && _sectionUtils.delay ? _sectionUtils.delay : (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
|
|
delayFn(transitionDelay).then(() => {
|
|
try {
|
|
// 모든 섹션 숨기기
|
|
if (_sectionDomUtils && _sectionDomUtils.addClasses) {
|
|
_sectionDomUtils.addClasses(section1Text, 'hidden');
|
|
_sectionDomUtils.addClasses(section2Text, 'hidden');
|
|
_sectionDomUtils.addClasses(section3Text, 'hidden');
|
|
} else {
|
|
section1Text.classList.add("hidden");
|
|
section2Text.classList.add("hidden");
|
|
section3Text.classList.add("hidden");
|
|
}
|
|
|
|
if (_sectionDomUtils && _sectionDomUtils.removeClasses) {
|
|
_sectionDomUtils.removeClasses(scrollIndicator, 'sec2');
|
|
_sectionDomUtils.removeClasses(section1Text, 'fade-out');
|
|
_sectionDomUtils.removeClasses(section2Text, 'fade-out');
|
|
_sectionDomUtils.removeClasses(section3Text, 'fade-out');
|
|
} else {
|
|
scrollIndicator.classList.remove("sec2");
|
|
section1Text.classList.remove("fade-out");
|
|
section2Text.classList.remove("fade-out");
|
|
section3Text.classList.remove("fade-out");
|
|
}
|
|
|
|
// 목표 섹션 표시
|
|
if (index === 0) {
|
|
if (_sectionDomUtils && _sectionDomUtils.removeClasses) {
|
|
_sectionDomUtils.removeClasses(section1Text, 'hidden');
|
|
_sectionDomUtils.removeClasses(scrollIndicator, 'hidden');
|
|
} else {
|
|
section1Text.classList.remove("hidden");
|
|
scrollIndicator.classList.remove("hidden");
|
|
}
|
|
|
|
if (_sectionDomUtils && _sectionDomUtils.setStyles) {
|
|
_sectionDomUtils.setStyles(cardsContainer, { display: 'none' });
|
|
_sectionDomUtils.setStyles(ctaBtn, { display: 'none' });
|
|
} else {
|
|
cardsContainer.style.display = "none";
|
|
ctaBtn.style.display = "none";
|
|
}
|
|
} else if (index === 1) {
|
|
if (_sectionDomUtils && _sectionDomUtils.addClasses) {
|
|
_sectionDomUtils.addClasses(scrollIndicator, 'sec2');
|
|
_sectionDomUtils.removeClasses(section2Text, 'hidden');
|
|
_sectionDomUtils.removeClasses(scrollIndicator, 'hidden');
|
|
} else {
|
|
scrollIndicator.classList.add("sec2");
|
|
section2Text.classList.remove("hidden");
|
|
scrollIndicator.classList.remove("hidden");
|
|
}
|
|
|
|
// 모바일(≤992px): section2Text 먼저 보여준 뒤 animateSection2에서 카드 표시
|
|
const isMobile = window.innerWidth <= 992;
|
|
if (_sectionDomUtils && _sectionDomUtils.setStyles) {
|
|
_sectionDomUtils.setStyles(cardsContainer, { display: isMobile ? 'none' : 'flex' });
|
|
_sectionDomUtils.setStyles(ctaBtn, { display: 'none' });
|
|
} else {
|
|
cardsContainer.style.display = isMobile ? "none" : "flex";
|
|
ctaBtn.style.display = "none";
|
|
}
|
|
|
|
resetSection2();
|
|
setTimeout(animateSection2, 200);
|
|
} else if (index === 2) {
|
|
if (_sectionDomUtils && _sectionDomUtils.removeClasses) {
|
|
_sectionDomUtils.removeClasses(section3Text, 'hidden');
|
|
_sectionDomUtils.addClasses(scrollIndicator, 'hidden');
|
|
_sectionDomUtils.removeClasses(scrollIndicator, 'sec2');
|
|
} else {
|
|
section3Text.classList.remove("hidden");
|
|
scrollIndicator.classList.add("hidden");
|
|
scrollIndicator.classList.remove("sec2");
|
|
}
|
|
|
|
if (_sectionDomUtils && _sectionDomUtils.setStyles) {
|
|
_sectionDomUtils.setStyles(cardsContainer, { display: 'none' });
|
|
_sectionDomUtils.setStyles(ctaBtn, { display: 'block' });
|
|
} else {
|
|
cardsContainer.style.display = "none";
|
|
ctaBtn.style.display = "block";
|
|
}
|
|
|
|
resetSection3();
|
|
setTimeout(animateSection3, 200);
|
|
}
|
|
|
|
INTRO_STATE.currentSection = index;
|
|
|
|
// 애니메이션 완료 플래그 리셋
|
|
const resetDelay = 500;
|
|
delayFn(resetDelay).then(() => {
|
|
INTRO_STATE.isAnimating = false;
|
|
});
|
|
} catch (error) {
|
|
_handleError(error, 'goToSection.transition');
|
|
INTRO_STATE.isAnimating = false;
|
|
}
|
|
});
|
|
} catch (error) {
|
|
_handleError(error, 'goToSection');
|
|
if (typeof INTRO_STATE !== 'undefined') {
|
|
INTRO_STATE.isAnimating = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 다음 섹션으로 스크롤
|
|
*/
|
|
function handleScrollDown() {
|
|
try {
|
|
if (typeof INTRO_STATE === 'undefined') {
|
|
_handleError(new Error('INTRO_STATE가 정의되지 않았습니다'), 'handleScrollDown');
|
|
return;
|
|
}
|
|
|
|
if (INTRO_STATE.isScrolling || INTRO_STATE.isAnimating) return;
|
|
|
|
const delayFn = _sectionUtils && _sectionUtils.delay ? _sectionUtils.delay : (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
const scrollCooldown = 700;
|
|
|
|
if (INTRO_STATE.currentSection === 0) {
|
|
INTRO_STATE.isScrolling = true;
|
|
delayFn(scrollCooldown).then(() => {
|
|
INTRO_STATE.isScrolling = false;
|
|
});
|
|
goToSection(1);
|
|
} else if (INTRO_STATE.currentSection === 1 && INTRO_STATE.section2AnimDone) {
|
|
INTRO_STATE.isScrolling = true;
|
|
delayFn(scrollCooldown).then(() => {
|
|
INTRO_STATE.isScrolling = false;
|
|
});
|
|
goToSection(2);
|
|
}
|
|
} catch (error) {
|
|
_handleError(error, 'handleScrollDown');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 이전 섹션으로 스크롤
|
|
*/
|
|
function handleScrollUp() {
|
|
try {
|
|
if (typeof INTRO_STATE === 'undefined') {
|
|
_handleError(new Error('INTRO_STATE가 정의되지 않았습니다'), 'handleScrollUp');
|
|
return;
|
|
}
|
|
|
|
if (INTRO_STATE.isScrolling || INTRO_STATE.isAnimating) return;
|
|
|
|
INTRO_STATE.isScrolling = true;
|
|
const delayFn = _sectionUtils && _sectionUtils.delay ? _sectionUtils.delay : (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
const scrollCooldown = 700;
|
|
|
|
delayFn(scrollCooldown).then(() => {
|
|
INTRO_STATE.isScrolling = false;
|
|
});
|
|
|
|
if (INTRO_STATE.currentSection === 2) {
|
|
goToSection(1);
|
|
} else if (INTRO_STATE.currentSection === 1) {
|
|
goToSection(0);
|
|
}
|
|
} catch (error) {
|
|
_handleError(error, 'handleScrollUp');
|
|
}
|
|
} |