// Main JavaScript for Deployr Landing Page // Initialize when DOM is loaded document.addEventListener('DOMContentLoaded', function() { initializeAnimations(); initializeScrollEffects(); initializeParallax(); initializeParticles(); initializeTypingEffect(); initializeCountUp(); initializeModalHandlers(); }); // Initialize animations function initializeAnimations() { // Intersection Observer for fade-in animations const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('animate-fade-in-up'); observer.unobserve(entry.target); } }); }, observerOptions); // Observe all service cards and sections document.querySelectorAll('.service-card, section > div').forEach(el => { observer.observe(el); }); } // Scroll effects function initializeScrollEffects() { let lastScrollY = window.scrollY; let ticking = false; function updateScrollEffects() { const scrollY = window.scrollY; const navbar = document.querySelector('nav'); // Navbar background on scroll if (scrollY > 50) { navbar.classList.add('bg-dark/95', 'backdrop-blur-lg', 'shadow-lg'); } else { navbar.classList.remove('bg-dark/95', 'backdrop-blur-lg', 'shadow-lg'); } lastScrollY = scrollY; ticking = false; } function requestTick() { if (!ticking) { window.requestAnimationFrame(updateScrollEffects); ticking = true; } } window.addEventListener('scroll', requestTick); } // Parallax effect function initializeParallax() { const parallaxElements = document.querySelectorAll('[data-parallax]'); function updateParallax() { const scrolled = window.pageYOffset; parallaxElements.forEach(element => { const speed = element.dataset.speed || 0.5; const yPos = -(scrolled * speed); element.style.transform = `translateY(${yPos}px)`; }); } window.addEventListener('scroll', updateParallax); } // Particle effects function initializeParticles() { const particleContainer = document.createElement('div'); particleContainer.className = 'fixed inset-0 pointer-events-none z-0'; document.body.appendChild(particleContainer); for (let i = 0; i < 20; i++) { setTimeout(() => { const particle = document.createElement('div'); particle.className = 'particle'; particle.style.left = Math.random() * 100 + '%'; particle.style.animationDelay = Math.random() * 10 + 's'; particle.style.animationDuration = (10 + Math.random() * 10) + 's'; particleContainer.appendChild(particle); }, i * 200); } } // Typing effect function initializeTypingEffect() { const typingElement = document.querySelector('[data-typing]'); if (!typingElement) return; const text = typingElement.getAttribute('data-typing'); let index = 0; function type() { if (index < text.length) { typingElement.textContent += text.charAt(index); index++; setTimeout(type, 100); } } type(); } // Count up animation function initializeCountUp() { const countElements = document.querySelectorAll('[data-count]'); const countObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const target = parseInt(entry.target.getAttribute('data-count')); const duration = 2000; const increment = target / (duration / 16); let current = 0; const updateCount = () => { if (current < target) { current += increment; entry.target.textContent = Math.floor(current); requestAnimationFrame(updateCount); } else { entry.target.textContent = target; } }; updateCount(); countObserver.unobserve(entry.target); } }); }); countElements.forEach(el => countObserver.observe(el)); } // Modal handlers function initializeModalHandlers() { // Open modal buttons document.querySelectorAll('[data-modal]').forEach(button => { button.addEventListener('click', () => { const modalId = button.getAttribute('data-modal'); const modal = document.getElementById(modalId); if (modal) { modal.classList.remove('hidden'); document.body.style.overflow = 'hidden'; } }); }); // Close modal buttons document.querySelectorAll('[data-close-modal]').forEach(button => { button.addEventListener('click', () => { const modal = button.closest('.modal'); if (modal) { modal.classList.add('hidden'); document.body.style.overflow = 'auto'; } }); }); // Close modal on backdrop click document.querySelectorAll('.modal').forEach(modal => { modal.addEventListener('click', (e) => { if (e.target === modal) { modal.classList.add('hidden'); document.body.style.overflow = 'auto'; } }); }); } // Smooth scroll for anchor 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' }); } }); }); // Form validation function validateForm(formId) { const form = document.getElementById(formId); if (!form) return false; const inputs = form.querySelectorAll('input[required], textarea[required]'); let isValid = true; inputs.forEach(input => { if (!input.value.trim()) { input.classList.add('border-red-500'); isValid = false; } else { input.classList.remove('border-red-500'); } }); return isValid; } // Loading state for buttons function setLoading(buttonId, loading = true) { const button = document.getElementById(buttonId); if (!button) return; if (loading) { button.disabled = true; button.classList.add('opacity-50', 'cursor-not-allowed'); button.innerHTML = 'Loading...'; } else { button.disabled = false; button.classList.remove('opacity-50', 'cursor-not-allowed'); button.innerHTML = button.getAttribute('data-original-text') || 'Submit'; } } // Toast notification function showToast(message, type = 'success') { const toast = document.createElement('div'); toast.className = `fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg transform transition-all duration-300 z-50 ${ type === 'success' ? 'bg-green-500' : 'bg-red-500' } text-white`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.classList.add('translate-y-0', 'opacity-100'); }, 100); setTimeout(() => { toast.classList.add('translate-y-full', 'opacity-0'); setTimeout(() => toast.remove(), 300); }, 3000); } // API call helper async function apiCall(url, options = {}) { try { const response = await fetch(url, { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error('API call failed:', error); showToast('An error occurred. Please try again.', 'error'); throw error; } } // Initialize GSAP animations if available if (typeof gsap !== 'undefined') { gsap.registerPlugin(ScrollTrigger); // Hero section animation gsap.timeline() .from('.hero h1', { y: 50, opacity: 0, duration: 1 }) .from('.hero p', { y: 30, opacity: 0, duration: 0.8 }, '-=0.5') .from('.hero .cta', { y: 20, opacity: 0, duration: 0.6 }, '-=0.3'); } // Theme toggle (if implemented) function toggleTheme() { document.body.classList.toggle('light-theme'); const isLight = document.body.classList.contains('light-theme'); localStorage.setItem('theme', isLight ? 'light' : 'dark'); } // Load saved theme const savedTheme = localStorage.getItem('theme'); if (savedTheme === 'light') { document.body.classList.add('light-theme'); } // Analytics tracking function trackEvent(eventName, properties = {}) { if (typeof gtag !== 'undefined') { gtag('event', eventName, properties); } } // Utility debouncing function function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Optimized resize handler const optimizedResize = debounce(() => { // Handle resize events }, 100); window.addEventListener('resize', optimizedResize); // Keyboard navigation document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { const openModal = document.querySelector('.modal:not(.hidden)'); if (openModal) { openModal.classList.add('hidden'); document.body.style.overflow = 'auto'; } } }); // Initialize tooltips function initializeTooltips() { document.querySelectorAll('[data-tooltip]').forEach(element => { element.addEventListener('mouseenter', (e) => { const tooltip = document.createElement('div'); tooltip.className = 'absolute z-50 px-2 py-1 text-xs bg-gray-800 text-white rounded'; tooltip.textContent = e.target.getAttribute('data-tooltip'); tooltip.style.bottom = '100%'; tooltip.style.left = '50%'; tooltip.style.transform = 'translateX(-50%)'; e.target.style.position = 'relative'; e.target.appendChild(tooltip); }); element.addEventListener('mouseleave', (e) => { const tooltip = e.target.querySelector('.absolute'); if (tooltip) tooltip.remove(); }); }); } // Initialize everything initializeTooltips();