// Shockwave animation const canvas = document.getElementById('shockwaveCanvas'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const shockwaves = []; class Shockwave { constructor(x, y) { this.x = x; this.y = y; this.radius = 0; this.maxRadius = 300; this.speed = 2; this.opacity = 0.5; this.color = `rgba(16, 185, 129, ${this.opacity})`; } update() { this.radius += this.speed; this.opacity = 0.5 * (1 - this.radius / this.maxRadius); this.color = `rgba(16, 185, 129, ${this.opacity})`; } draw() { ctx.strokeStyle = this.color; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); ctx.stroke(); // Second ring ctx.strokeStyle = `rgba(59, 130, 246, ${this.opacity * 0.5})`; ctx.beginPath(); ctx.arc(this.x, this.y, this.radius * 0.8, 0, Math.PI * 2); ctx.stroke(); } isDead() { return this.radius > this.maxRadius; } } function animateShockwaves() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Create new shockwave randomly if (Math.random() < 0.02) { shockwaves.push(new Shockwave( Math.random() * canvas.width, Math.random() * canvas.height )); } // Update and draw shockwaves for (let i = shockwaves.length - 1; i >= 0; i--) { const wave = shockwaves[i]; wave.update(); wave.draw(); if (wave.isDead()) { shockwaves.splice(i, 1); } } requestAnimationFrame(animateShockwaves); } animateShockwaves(); // Resize canvas on window resize window.addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }); // Add interactive shockwave on click document.addEventListener('click', (e) => { shockwaves.push(new Shockwave(e.clientX, e.clientY)); }); // Service filtering (only if on services page) if (document.querySelector('.filter-tag')) { const filterTags = document.querySelectorAll('.filter-tag'); const serviceCards = document.querySelectorAll('.service-card'); filterTags.forEach(tag => { tag.addEventListener('click', () => { const filter = tag.dataset.filter; // Update active state filterTags.forEach(t => t.classList.remove('bg-emerald-600', 'text-white')); tag.classList.add('bg-emerald-600', 'text-white'); // Filter cards serviceCards.forEach(card => { if (filter === 'all' || card.dataset.category === filter) { card.style.display = 'block'; setTimeout(() => card.classList.add('visible'), 100); } else { card.style.display = 'none'; card.classList.remove('visible'); } }); }); }); } // Timeline animation on scroll const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -100px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); } }); }, observerOptions); // Observe elements for animation document.querySelectorAll('.timeline-item, .service-card, .skill-bar').forEach(el => { observer.observe(el); }); // Testimonial slider let currentSlide = 0; const testimonialsContainer = document.getElementById('testimonialsContainer'); const totalSlides = 3; function goToSlide(slideIndex) { currentSlide = slideIndex; testimonialsContainer.style.transform = `translateX(-${slideIndex * 100}%)`; // Update dots document.querySelectorAll('.testimonials-slider button').forEach((dot, index) => { if (index === slideIndex) { dot.classList.remove('bg-gray-600'); dot.classList.add('bg-emerald-500'); } else { dot.classList.remove('bg-emerald-500'); dot.classList.add('bg-gray-600'); } }); } // Auto-advance testimonials setInterval(() => { currentSlide = (currentSlide + 1) % totalSlides; goToSlide(currentSlide); }, 5000); // Contact form submission document.getElementById('contactForm').addEventListener('submit', async (e) => { e.preventDefault(); const formData = new FormData(e.target); const data = Object.fromEntries(formData); // Show loading state const submitBtn = e.target.querySelector('button[type="submit"]'); const originalText = submitBtn.textContent; submitBtn.innerHTML = ' Sending...'; submitBtn.disabled = true; try { // Simulate API call await new Promise(resolve => setTimeout(resolve, 2000)); // Show success message const successMsg = document.getElementById('successMessage'); successMsg.style.transform = 'translateX(0)'; // Reset form e.target.reset(); // Hide success message after 3 seconds setTimeout(() => { successMsg.style.transform = 'translateX(100%)'; }, 3000); } catch (error) { console.error('Error:', error); alert('Error sending message. Please try again.'); } finally { // Reset button submitBtn.textContent = originalText; submitBtn.disabled = false; } }); // FAQ toggle function window.toggleFAQ = function(button) { const content = button.nextElementSibling; const icon = button.querySelector('i'); content.classList.toggle('hidden'); icon.style.transform = content.classList.contains('hidden') ? 'rotate(0deg)' : 'rotate(180deg)'; }; // Smooth scroll 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' }); } }); }); // Parallax scrolling effect window.addEventListener('scroll', () => { const scrolled = window.pageYOffset; const parallaxElements = document.querySelectorAll('.parallax'); parallaxElements.forEach(el => { const speed = el.dataset.speed || 0.5; el.style.transform = `translateY(${scrolled * speed}px)`; }); }); // Add reveal animation to elements on scroll const revealElements = document.querySelectorAll('.service-card, .timeline-item'); const revealOnScroll = () => { revealElements.forEach(element => { const elementTop = element.getBoundingClientRect().top; const elementBottom = element.getBoundingClientRect().bottom; if (elementTop < window.innerHeight && elementBottom > 0) { element.classList.add('visible'); } }); }; window.addEventListener('scroll', revealOnScroll); revealOnScroll(); // Initial check // Dynamic skill bar animation const skillBars = document.querySelectorAll('.skill-bar'); const animateSkillBars = () => { skillBars.forEach(bar => { const rect = bar.getBoundingClientRect(); if (rect.top < window.innerHeight && rect.bottom > 0) { const progressBar = bar.querySelector('.bg-gradient-to-r'); const width = progressBar.style.width || '0%'; if (width === '0%') { progressBar.style.width = progressBar.parentElement.previousElementSibling.querySelector('span:last-child').textContent; } } }); }; window.addEventListener('scroll', animateSkillBars); animateSkillBars(); // Keyboard navigation support document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { closeCaseStudy(); } }); // Performance optimization - Debounce scroll events let scrollTimeout; window.addEventListener('scroll', () => { if (scrollTimeout) { window.cancelAnimationFrame(scrollTimeout); } scrollTimeout = window.requestAnimationFrame(() => { // Scroll-based animations here revealOnScroll(); animateSkillBars(); }); });