// Mobile Navigation Toggle const hamburger = document.querySelector('.hamburger'); const navMenu = document.querySelector('.nav-menu'); hamburger.addEventListener('click', () => { hamburger.classList.toggle('active'); navMenu.classList.toggle('active'); }); // Close mobile menu when clicking on a link document.querySelectorAll('.nav-link').forEach(link => { link.addEventListener('click', () => { hamburger.classList.remove('active'); navMenu.classList.remove('active'); }); }); // Smooth scrolling for navigation links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // Active navigation link on scroll window.addEventListener('scroll', () => { let current = ''; const sections = document.querySelectorAll('section'); sections.forEach(section => { const sectionTop = section.offsetTop; const sectionHeight = section.clientHeight; if (scrollY >= (sectionTop - 200)) { current = section.getAttribute('id'); } }); document.querySelectorAll('.nav-link').forEach(link => { link.classList.remove('active'); if (link.getAttribute('href').slice(1) === current) { link.classList.add('active'); } }); }); // Header background on scroll window.addEventListener('scroll', () => { const navbar = document.querySelector('.navbar'); if (window.scrollY > 50) { navbar.style.background = 'rgba(255, 255, 255, 0.95)'; navbar.style.backdropFilter = 'blur(10px)'; } else { navbar.style.background = 'white'; navbar.style.backdropFilter = 'none'; } }); // Form submission handling const contactForm = document.querySelector('.contact-form form'); if (contactForm) { contactForm.addEventListener('submit', function(e) { e.preventDefault(); // Get form data const formData = new FormData(this); const name = this.querySelector('input[type="text"]').value; const email = this.querySelector('input[type="email"]').value; const phone = this.querySelector('input[type="tel"]').value; const service = this.querySelector('select').value; const message = this.querySelector('textarea').value; // Simple validation if (!name || !email || !message) { showNotification('Por favor, completa todos los campos obligatorios.', 'error'); return; } // Email validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { showNotification('Por favor, introduce un email válido.', 'error'); return; } // Simulate form submission (replace with actual endpoint) showNotification('¡Mensaje enviado con éxito! Nos pondremos en contacto contigo pronto.', 'success'); this.reset(); }); } // Newsletter form handling const newsletterForm = document.querySelector('.newsletter-form'); if (newsletterForm) { newsletterForm.addEventListener('submit', function(e) { e.preventDefault(); const email = this.querySelector('input[type="email"]').value; if (!email) { showNotification('Por favor, introduce tu email.', 'error'); return; } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { showNotification('Por favor, introduce un email válido.', 'error'); return; } // Simulate newsletter subscription showNotification('¡Gracias por suscribirte a nuestro newsletter!', 'success'); this.reset(); }); } // Notification system function showNotification(message, type = 'info') { // Remove existing notifications const existingNotification = document.querySelector('.notification'); if (existingNotification) { existingNotification.remove(); } // Create notification element const notification = document.createElement('div'); notification.className = `notification notification-${type}`; notification.textContent = message; // Add styles notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 15px 20px; border-radius: 8px; color: white; font-weight: 500; z-index: 10000; transform: translateX(100%); transition: transform 0.3s ease; max-width: 300px; `; // Set background color based on type switch(type) { case 'success': notification.style.background = '#10b981'; break; case 'error': notification.style.background = '#ef4444'; break; default: notification.style.background = '#3b82f6'; } // Add to page document.body.appendChild(notification); // Animate in setTimeout(() => { notification.style.transform = 'translateX(0)'; }, 100); // Remove after 5 seconds setTimeout(() => { notification.style.transform = 'translateX(100%)'; setTimeout(() => { notification.remove(); }, 300); }, 5000); } // Intersection Observer for animations const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; } }); }, observerOptions); // Observe elements for animation document.addEventListener('DOMContentLoaded', () => { const animatedElements = document.querySelectorAll('.service-card, .project-card, .testimonial-card'); animatedElements.forEach(el => { el.style.opacity = '0'; el.style.transform = 'translateY(30px)'; el.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; observer.observe(el); }); }); // Lazy loading for images const imageObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.style.opacity = '0'; img.style.transition = 'opacity 0.5s ease'; img.onload = () => { img.style.opacity = '1'; }; // Force load if not already loaded if (img.complete) { img.style.opacity = '1'; } imageObserver.unobserve(img); } }); }); // Observe all images document.addEventListener('DOMContentLoaded', () => { const images = document.querySelectorAll('img'); images.forEach(img => imageObserver.observe(img)); }); // Parallax effect for hero section window.addEventListener('scroll', () => { const scrolled = window.pageYOffset; const hero = document.querySelector('.hero'); if (hero) { hero.style.transform = `translateY(${scrolled * 0.5}px)`; } }); // Add loading animation window.addEventListener('load', () => { document.body.style.opacity = '0'; document.body.style.transition = 'opacity 0.5s ease'; setTimeout(() => { document.body.style.opacity = '1'; }, 100); }); // Counter animation for statistics function animateCounter(element, target, duration = 2000) { let start = 0; const increment = target / (duration / 16); const timer = setInterval(() => { start += increment; if (start >= target) { element.textContent = target; clearInterval(timer); } else { element.textContent = Math.floor(start); } }, 16); } // Initialize counters when in viewport const counterObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting && !entry.target.classList.contains('counted')) { const target = parseInt(entry.target.textContent); entry.target.classList.add('counted'); animateCounter(entry.target, target); } }); }, { threshold: 0.5 }); // Add hover effects to cards document.addEventListener('DOMContentLoaded', () => { const cards = document.querySelectorAll('.service-card, .project-card, .testimonial-card'); cards.forEach(card => { card.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-10px) scale(1.02)'; }); card.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0) scale(1)'; }); }); }); // Add active class to current navigation item document.addEventListener('DOMContentLoaded', () => { const currentPath = window.location.hash || '#inicio'; const activeLink = document.querySelector(`.nav-link[href="${currentPath}"]`); if (activeLink) { activeLink.classList.add('active'); } });