// DOM이 로드된 후 실행 document.addEventListener('DOMContentLoaded', function() { // 모바일 네비게이션 토글 const hamburger = document.querySelector('.hamburger'); const navMenu = document.querySelector('.nav-menu'); if (hamburger) { hamburger.addEventListener('click', function() { hamburger.classList.toggle('active'); navMenu.classList.toggle('active'); }); } // 네비게이션 링크 클릭 시 모바일 메뉴 닫기 document.querySelectorAll('.nav-link').forEach(link => { link.addEventListener('click', () => { if (hamburger) hamburger.classList.remove('active'); if (navMenu) navMenu.classList.remove('active'); }); }); // 스무스 스크롤 (리디렉션 방지) - 개선된 버전 document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const targetId = this.getAttribute('href'); const target = document.querySelector(targetId); if (target) { // URL 업데이트 (히스토리 API 사용) - 리디렉션 방지 if (history.pushState) { history.pushState(null, null, targetId); } // 스무스 스크롤 const headerOffset = 80; // 네비게이션 높이 고려 const elementPosition = target.getBoundingClientRect().top; const offsetPosition = elementPosition + window.pageYOffset - headerOffset; window.scrollTo({ top: offsetPosition, behavior: 'smooth' }); } }); }); // 브라우저 뒤로가기/앞으로가기 처리 window.addEventListener('popstate', function(e) { const hash = window.location.hash; if (hash) { const target = document.querySelector(hash); if (target) { const headerOffset = 80; const elementPosition = target.getBoundingClientRect().top; const offsetPosition = elementPosition + window.pageYOffset - headerOffset; window.scrollTo({ top: offsetPosition, behavior: 'smooth' }); } } }); // 페이지 로드 시 해시가 있으면 해당 섹션으로 스크롤 if (window.location.hash) { setTimeout(() => { const target = document.querySelector(window.location.hash); if (target) { const headerOffset = 80; const elementPosition = target.getBoundingClientRect().top; const offsetPosition = elementPosition + window.pageYOffset - headerOffset; window.scrollTo({ top: offsetPosition, behavior: 'smooth' }); } }, 100); } // 네비게이션 스크롤 효과 window.addEventListener('scroll', function() { const navbar = document.querySelector('.navbar'); if (navbar) { if (window.scrollY > 100) { navbar.style.background = 'rgba(255, 255, 255, 0.98)'; navbar.style.boxShadow = '0 2px 20px rgba(0, 0, 0, 0.1)'; } else { navbar.style.background = 'rgba(255, 255, 255, 0.95)'; navbar.style.boxShadow = 'none'; } } }); // 폼 제출 처리 const contactForm = document.querySelector('.contact-form form'); if (contactForm) { contactForm.addEventListener('submit', function(e) { e.preventDefault(); // 폼 데이터 수집 const formData = new FormData(this); const name = this.querySelector('input[type="text"]')?.value || ''; const email = this.querySelector('input[type="email"]')?.value || ''; const subject = this.querySelectorAll('input[type="text"]')[1]?.value || ''; const message = this.querySelector('textarea')?.value || ''; // 간단한 유효성 검사 if (!name || !email || !subject || !message) { showNotification('모든 필드를 입력해주세요.', 'error'); return; } if (!isValidEmail(email)) { showNotification('올바른 이메일 주소를 입력해주세요.', 'error'); return; } // 성공 메시지 표시 (실제로는 서버로 전송) showNotification('메시지가 성공적으로 전송되었습니다!', 'success'); this.reset(); }); } // 이메일 유효성 검사 함수 function isValidEmail(email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } // 알림 표시 함수 function showNotification(message, type) { // 기존 알림 제거 const existingNotification = document.querySelector('.notification'); if (existingNotification) { existingNotification.remove(); } // 새 알림 생성 const notification = document.createElement('div'); notification.className = `notification ${type}`; notification.textContent = message; // 스타일 적용 notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 1rem 2rem; border-radius: 10px; color: white; font-weight: 600; z-index: 10000; transform: translateX(100%); transition: transform 0.3s ease; ${type === 'success' ? 'background: #10b981;' : 'background: #ef4444;'} `; document.body.appendChild(notification); // 애니메이션 setTimeout(() => { notification.style.transform = 'translateX(0)'; }, 100); // 자동 제거 setTimeout(() => { notification.style.transform = 'translateX(100%)'; setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 300); }, 3000); } // 포트폴리오 아이템 호버 효과 const portfolioItems = document.querySelectorAll('.portfolio-item'); portfolioItems.forEach(item => { item.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-10px)'; }); item.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; }); }); // 서비스 카드 호버 효과 const serviceCards = document.querySelectorAll('.service-card'); serviceCards.forEach(card => { card.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-10px)'; }); card.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; }); }); // 통계 숫자 애니메이션 const statItems = document.querySelectorAll('.stat-item h3'); const observerOptions = { threshold: 0.5, rootMargin: '0px 0px -100px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const target = entry.target; const finalValue = parseInt(target.textContent); animateNumber(target, 0, finalValue, 2000); observer.unobserve(target); } }); }, observerOptions); statItems.forEach(item => { observer.observe(item); }); // 숫자 애니메이션 함수 function animateNumber(element, start, end, duration) { const startTime = performance.now(); function updateNumber(currentTime) { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); const current = Math.floor(start + (end - start) * progress); element.textContent = current + (element.textContent.includes('+') ? '+' : ''); if (progress < 1) { requestAnimationFrame(updateNumber); } } requestAnimationFrame(updateNumber); } // 스크롤 시 요소 페이드인 효과 const fadeElements = document.querySelectorAll('.service-card, .portfolio-item, .about-text, .about-stats'); const fadeObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; } }); }, { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }); fadeElements.forEach(element => { element.style.opacity = '0'; element.style.transform = 'translateY(30px)'; element.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; fadeObserver.observe(element); }); // 로딩 완료 후 초기 애니메이션 setTimeout(() => { document.body.style.opacity = '1'; }, 100); }); // 페이지 로드 시 초기 설정 window.addEventListener('load', function() { // 페이지 로딩 완료 후 추가 효과 const heroTitle = document.querySelector('.hero-title'); const heroSubtitle = document.querySelector('.hero-subtitle'); const heroButtons = document.querySelector('.hero-buttons'); if (heroTitle) { setTimeout(() => { heroTitle.style.opacity = '1'; heroTitle.style.transform = 'translateY(0)'; }, 500); } if (heroSubtitle) { setTimeout(() => { heroSubtitle.style.opacity = '1'; heroSubtitle.style.transform = 'translateY(0)'; }, 700); } if (heroButtons) { setTimeout(() => { heroButtons.style.opacity = '1'; heroButtons.style.transform = 'translateY(0)'; }, 900); } });