Spaces:
Running
Running
| // Initialize on DOM load | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Generate stars for parallax background | |
| generateStars(); | |
| // Initialize gallery | |
| populateGallery(); | |
| // Initialize counters | |
| initCounters(); | |
| // Initialize form | |
| initContactForm(); | |
| // Initialize scroll effects | |
| initScrollEffects(); | |
| // Initialize parallax | |
| initParallax(); | |
| // Initialize typewriter effect | |
| initTypewriter(); | |
| }); | |
| // Initialize typewriter effect | |
| function initTypewriter() { | |
| const texts = [ | |
| "Where Dreams Meet Digital Reality", | |
| "Where Innovation Meets Imagination", | |
| "Where Code Meets Creativity", | |
| "Where Vision Meets Technology" | |
| ]; | |
| let textIndex = 0; | |
| let charIndex = 0; | |
| let isDeleting = false; | |
| let typewriterElement = document.getElementById('typewriter-text'); | |
| if (!typewriterElement) return; | |
| function type() { | |
| const currentText = texts[textIndex]; | |
| if (isDeleting) { | |
| typewriterElement.textContent = currentText.substring(0, charIndex - 1); | |
| charIndex--; | |
| } else { | |
| typewriterElement.textContent = currentText.substring(0, charIndex + 1); | |
| charIndex++; | |
| } | |
| let typeSpeed = isDeleting ? 50 : 100; | |
| if (!isDeleting && charIndex === currentText.length) { | |
| typeSpeed = 2000; // Pause at end | |
| isDeleting = true; | |
| } else if (isDeleting && charIndex === 0) { | |
| isDeleting = false; | |
| textIndex = (textIndex + 1) % texts.length; | |
| typeSpeed = 500; // Pause before typing new text | |
| } | |
| setTimeout(type, typeSpeed); | |
| } | |
| // Start the typewriter effect | |
| type(); | |
| } | |
| // Generate animated stars | |
| function generateStars() { | |
| const parallaxBg = document.getElementById('parallaxBg'); | |
| const starCount = 100; | |
| for (let i = 0; i < starCount; i++) { | |
| const star = document.createElement('div'); | |
| star.className = 'star'; | |
| star.style.width = Math.random() * 3 + 'px'; | |
| star.style.height = star.style.width; | |
| star.style.left = Math.random() * 100 + '%'; | |
| star.style.top = Math.random() * 100 + '%'; | |
| star.style.animationDelay = Math.random() * 3 + 's'; | |
| parallaxBg.appendChild(star); | |
| } | |
| } | |
| // Populate gallery with dynamic content | |
| function populateGallery() { | |
| const gallery = document.getElementById('gallery'); | |
| const categories = ['nature', 'technology', 'abstract', 'cityscape', 'workspace', 'food']; | |
| const colors = ['purple', 'blue', 'green', 'pink', 'yellow', 'red']; | |
| for (let i = 0; i < 9; i++) { | |
| const card = document.createElement('div'); | |
| const category = categories[i % categories.length]; | |
| const color = colors[i % colors.length]; | |
| const imageUrl = `https://static.photos/${category}/640x360/${i + 42}`; | |
| card.className = 'card-hover glass-morphism rounded-2xl overflow-hidden group cursor-pointer'; | |
| card.innerHTML = ` | |
| <div class="relative h-64 overflow-hidden"> | |
| <img src="${imageUrl}" alt="Gallery Item ${i + 1}" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"> | |
| <div class="absolute inset-0 bg-gradient-to-t from-gray-900 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div> | |
| <div class="absolute bottom-0 left-0 p-6 transform translate-y-full group-hover:translate-y-0 transition-transform duration-300"> | |
| <h3 class="text-xl font-bold mb-2">Masterpiece ${i + 1}</h3> | |
| <p class="text-sm text-gray-300">Click to explore infinite possibilities</p> | |
| </div> | |
| </div> | |
| `; | |
| card.addEventListener('click', function() { | |
| createParticleEffect(event); | |
| }); | |
| gallery.appendChild(card); | |
| } | |
| } | |
| // Initialize animated counters | |
| function initCounters() { | |
| const counters = document.querySelectorAll('.counter'); | |
| const speed = 200; | |
| const observerOptions = { | |
| threshold: 0.5 | |
| }; | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| const counter = entry.target; | |
| const target = +counter.getAttribute('data-target'); | |
| const increment = target / speed; | |
| const updateCount = () => { | |
| const count = +counter.innerText; | |
| if (count < target) { | |
| counter.innerText = Math.ceil(count + increment); | |
| setTimeout(updateCount, 1); | |
| } else { | |
| counter.innerText = target + '+'; | |
| } | |
| }; | |
| updateCount(); | |
| observer.unobserve(counter); | |
| } | |
| }); | |
| }, observerOptions); | |
| counters.forEach(counter => observer.observe(counter)); | |
| } | |
| // Initialize contact form | |
| function initContactForm() { | |
| const form = document.getElementById('contactForm'); | |
| form.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| // Create success message | |
| const successMsg = document.createElement('div'); | |
| successMsg.className = 'fixed top-20 right-6 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg z-50 fade-in'; | |
| successMsg.innerHTML = ` | |
| <div class="flex items-center gap-2"> | |
| <i data-feather="check-circle" class="w-5 h-5"></i> | |
| <span>Message sent successfully!</span> | |
| </div> | |
| `; | |
| document.body.appendChild(successMsg); | |
| feather.replace(); | |
| // Reset form | |
| form.reset(); | |
| // Remove message after 3 seconds | |
| setTimeout(() => { | |
| successMsg.remove(); | |
| }, 3000); | |
| // Create particle effect | |
| createParticleEffect(e); | |
| }); | |
| } | |
| // Initialize scroll effects | |
| function initScrollEffects() { | |
| 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('fade-in'); | |
| } | |
| }); | |
| }, observerOptions); | |
| document.querySelectorAll('section').forEach(section => { | |
| observer.observe(section); | |
| }); | |
| } | |
| // Initialize parallax effect | |
| function initParallax() { | |
| window.addEventListener('scroll', () => { | |
| const scrolled = window.pageYOffset; | |
| const parallaxBg = document.getElementById('parallaxBg'); | |
| parallaxBg.style.transform = `translateY(${scrolled * 0.5}px)`; | |
| }); | |
| } | |
| // Create particle effect on click | |
| function createParticleEffect(event) { | |
| const colors = ['#9333ea', '#ec4899', '#3b82f6', '#10b981', '#f59e0b']; | |
| const particleCount = 20; | |
| for (let i = 0; i < particleCount; i++) { | |
| const particle = document.createElement('div'); | |
| particle.className = 'particle'; | |
| const size = Math.random() * 8 + 4; | |
| const color = colors[Math.floor(Math.random() * colors.length)]; | |
| const angle = (Math.PI * 2 * i) / particleCount; | |
| const velocity = Math.random() * 100 + 50; | |
| particle.style.width = size + 'px'; | |
| particle.style.height = size + 'px'; | |
| particle.style.backgroundColor = color; | |
| particle.style.borderRadius = '50%'; | |
| particle.style.left = event.clientX + 'px'; | |
| particle.style.top = event.clientY + 'px'; | |
| particle.style.setProperty('--tx', Math.cos(angle) * velocity + 'px'); | |
| particle.style.setProperty('--ty', Math.sin(angle) * velocity + 'px'); | |
| document.body.appendChild(particle); | |
| setTimeout(() => particle.remove(), 3000); | |
| } | |
| } | |
| // Add cursor trail effect | |
| document.addEventListener('mousemove', (e) => { | |
| if (Math.random() > 0.98) { | |
| const trail = document.createElement('div'); | |
| trail.className = 'fixed w-2 h-2 bg-purple-500 rounded-full pointer-events-none z-40'; | |
| trail.style.left = e.clientX + 'px'; | |
| trail.style.top = e.clientY + 'px'; | |
| trail.style.animation = 'fadeOut 1s ease-out forwards'; | |
| document.body.appendChild(trail); | |
| setTimeout(() => trail.remove(), 1000); | |
| } | |
| }); | |
| // Earth interaction | |
| function initEarthInteraction() { | |
| const earthContainer = document.querySelector('.earth-container'); | |
| if (!earthContainer) return; | |
| let mouseX = 0; | |
| let mouseY = 0; | |
| let currentX = 0; | |
| let currentY = 0; | |
| document.addEventListener('mousemove', (e) => { | |
| const rect = earthContainer.getBoundingClientRect(); | |
| const centerX = rect.left + rect.width / 2; | |
| const centerY = rect.top + rect.height / 2; | |
| mouseX = (e.clientX - centerX) / 20; | |
| mouseY = (e.clientY - centerY) / 20; | |
| }); | |
| function animateEarth() { | |
| currentX += (mouseX - currentX) * 0.1; | |
| currentY += (mouseY - currentY) * 0.1; | |
| const earthSphere = earthContainer.querySelector('.earth-sphere'); | |
| if (earthSphere) { | |
| earthSphere.style.transform = `rotateY(${currentX}deg) rotateX(${-currentY}deg) rotateZ(-23.5deg)`; | |
| } | |
| requestAnimationFrame(animateEarth); | |
| } | |
| animateEarth(); | |
| // Add click event for earthquake effect | |
| earthContainer.addEventListener('click', function(e) { | |
| const earthSphere = this.querySelector('.earth-sphere'); | |
| earthSphere.style.animation = 'none'; | |
| setTimeout(() => { | |
| earthSphere.style.animation = 'earthRotation 30s linear infinite, earthquake 0.5s ease-in-out'; | |
| }, 10); | |
| setTimeout(() => { | |
| earthSphere.style.animation = 'earthRotation 30s linear infinite'; | |
| }, 500); | |
| createEarthParticles(e); | |
| }); | |
| } | |
| // Create Earth particles on click | |
| function createEarthParticles(event) { | |
| const colors = ['#4a90e2', '#228b22', '#87ceeb', '#ffffff']; | |
| const particleCount = 15; | |
| for (let i = 0; i < particleCount; i++) { | |
| const particle = document.createElement('div'); | |
| particle.className = 'earth-particle'; | |
| const size = Math.random() * 6 + 2; | |
| const color = colors[Math.floor(Math.random() * colors.length)]; | |
| const angle = (Math.PI * 2 * i) / particleCount; | |
| const velocity = Math.random() * 80 + 40; | |
| particle.style.width = size + 'px'; | |
| particle.style.height = size + 'px'; | |
| particle.style.backgroundColor = color; | |
| particle.style.borderRadius = '50%'; | |
| particle.style.position = 'fixed'; | |
| particle.style.pointerEvents = 'none'; | |
| particle.style.zIndex = '1000'; | |
| particle.style.left = event.clientX + 'px'; | |
| particle.style.top = event.clientY + 'px'; | |
| particle.style.boxShadow = `0 0 10px ${color}`; | |
| particle.style.setProperty('--tx', Math.cos(angle) * velocity + 'px'); | |
| particle.style.setProperty('--ty', Math.sin(angle) * velocity + 'px'); | |
| document.body.appendChild(particle); | |
| setTimeout(() => particle.remove(), 2000); | |
| } | |
| } | |
| // Add earthquake animation | |
| const earthquakeStyle = document.createElement('style'); | |
| earthquakeStyle.textContent = ` | |
| @keyframes earthquake { | |
| 0%, 100% { transform: rotateY(0deg) rotateX(0deg) translateX(0); } | |
| 25% { transform: rotateY(2deg) rotateX(-1deg) translateX(-2px); } | |
| 50% { transform: rotateY(-1deg) rotateX(2deg) translateX(2px); } | |
| 75% { transform: rotateY(1deg) rotateX(-2deg) translateX(-1px); } | |
| } | |
| .earth-particle { | |
| opacity: 1; | |
| animation: earthParticleAnimation 2s ease-out forwards; | |
| } | |
| @keyframes earthParticleAnimation { | |
| 0% { | |
| opacity: 1; | |
| transform: translate(0, 0) scale(0); | |
| } | |
| 50% { | |
| opacity: 1; | |
| transform: translate(var(--tx), var(--ty)) scale(1); | |
| } | |
| 100% { | |
| opacity: 0; | |
| transform: translate(calc(var(--tx) * 1.5), calc(var(--ty) * 1.5)) scale(0.5); | |
| } | |
| } | |
| `; | |
| document.head.appendChild(earthquakeStyle); | |
| // Initialize Earth interaction on DOM load | |
| document.addEventListener('DOMContentLoaded', function() { | |
| initEarthInteraction(); | |
| }); | |
| // Add keyboard navigation | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'Escape') { | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| } | |
| }); | |
| // Performance optimization - Debounce scroll events | |
| let scrollTimeout; | |
| window.addEventListener('scroll', () => { | |
| if (scrollTimeout) { | |
| window.cancelAnimationFrame(scrollTimeout); | |
| } | |
| scrollTimeout = window.requestAnimationFrame(() => { | |
| // Add any scroll-based animations here | |
| }); | |
| }); |