mingle-website/script.js

299 lines
10 KiB
JavaScript

// 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);
}
});