Spaces:
Running
Running
| // Quantum Canvas Animation | |
| const canvas = document.getElementById('quantumCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| let particles = []; | |
| let mouseX = 0; | |
| let mouseY = 0; | |
| function resizeCanvas() { | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| } | |
| class Particle { | |
| constructor() { | |
| this.x = Math.random() * canvas.width; | |
| this.y = Math.random() * canvas.height; | |
| this.vx = (Math.random() - 0.5) * 0.5; | |
| this.vy = (Math.random() - 0.5) * 0.5; | |
| this.radius = Math.random() * 2 + 1; | |
| this.hue = Math.random() * 360; | |
| } | |
| update() { | |
| this.x += this.vx; | |
| this.y += this.vy; | |
| if (this.x < 0 || this.x > canvas.width) this.vx *= -1; | |
| if (this.y < 0 || this.y > canvas.height) this.vy *= -1; | |
| // Mouse interaction | |
| const dx = mouseX - this.x; | |
| const dy = mouseY - this.y; | |
| const distance = Math.sqrt(dx * dx + dy * dy); | |
| if (distance < 100) { | |
| this.vx += dx * 0.0001; | |
| this.vy += dy * 0.0001; | |
| } | |
| this.hue += 0.5; | |
| } | |
| draw() { | |
| ctx.beginPath(); | |
| ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); | |
| ctx.fillStyle = `hsla(${this.hue}, 70%, 50%, 0.8)`; | |
| ctx.fill(); | |
| } | |
| } | |
| function initParticles() { | |
| particles = []; | |
| const particleCount = Math.min(100, Math.floor(canvas.width * canvas.height / 10000)); | |
| for (let i = 0; i < particleCount; i++) { | |
| particles.push(new Particle()); | |
| } | |
| } | |
| function animate() { | |
| ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| particles.forEach(particle => { | |
| particle.update(); | |
| particle.draw(); | |
| }); | |
| // Draw connections | |
| particles.forEach((p1, index) => { | |
| particles.slice(index + 1).forEach(p2 => { | |
| const dx = p1.x - p2.x; | |
| const dy = p1.y - p2.y; | |
| const distance = Math.sqrt(dx * dx + dy * dy); | |
| if (distance.distance < 100) { | |
| ctx.beginPath(); | |
| ctx.moveTo(p1.x, p1.y); | |
| ctx.lineTo(p2.x, p2.y); | |
| ctx.strokeStyle = `hsla(${(p1.hue + p2.hue) / 2}, 70%, 50%, ${0.2 * (1 - distance / 100)})`; | |
| ctx.stroke(); | |
| } | |
| }); | |
| }); | |
| requestAnimationFrame(animate); | |
| } | |
| // Event listeners | |
| window.addEventListener('resize', () => { | |
| resizeCanvas(); | |
| initParticles(); | |
| }); | |
| canvas.addEventListener('mousemove', (e) => { | |
| mouseX = e.clientX; | |
| mouseY = e.clientY; | |
| }); | |
| // Initialize | |
| resizeCanvas(); | |
| initParticles(); | |
| animate(); | |
| // Meditation Modal | |
| const enterPortalBtn = document.getElementById('enterPortal'); | |
| const startMeditationBtn = document.getElementById('startMeditation'); | |
| const meditationModal = document.getElementById('meditationModal'); | |
| const closeMeditationBtn = document.getElementById('closeMeditation'); | |
| const breathingCircle = document.getElementById('breathingCircle'); | |
| const breathText = document.getElementById('breathText'); | |
| let meditationInterval; | |
| let isInhaling = true; | |
| enterPortalBtn.addEventListener('click', () => { | |
| document.getElementById('dimensions').scrollIntoView({ behavior: 'smooth' }); | |
| }); | |
| startMeditationBtn.addEventListener('click', () => { | |
| meditationModal.classList.remove('hidden'); | |
| startBreathing(); | |
| }); | |
| closeMeditationBtn.addEventListener('click', () => { | |
| meditationModal.classList.add('hidden'); | |
| clearInterval(meditationInterval); | |
| }); | |
| function startBreathing() { | |
| let scale = 1; | |
| let speed = 0.005; | |
| meditationInterval = setInterval(() => { | |
| if (isInhaling) { | |
| scale += speed; | |
| breathText.textContent = 'Inhale'; | |
| if (scale >= 1.5) isInhaling = false; | |
| } else { | |
| scale -= speed; | |
| breathText.textContent = 'Exhale'; | |
| if (scale <= 1) isInhaling = true; | |
| } | |
| breathingCircle.style.transform = `scale(${scale})`; | |
| }, 16); | |
| } | |
| // Smooth scroll for navigation | |
| 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' }); | |
| } | |
| }); | |
| }); | |
| // 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.classList.add('animate-fade-in'); | |
| } | |
| }); | |
| }, observerOptions); | |
| document.querySelectorAll('section').forEach(section => { | |
| observer.observe(section); | |
| }); | |
| // Add ripple effect on click | |
| document.addEventListener('click', (e) => { | |
| const ripple = document.createElement('div'); | |
| ripple.className = 'fixed w-4 h-4 rounded-full bg-purple-400/50 pointer-events-none animate-ping'; | |
| ripple.style.left = e.clientX - 8 + 'px'; | |
| ripple.style.top = e.clientY - 8 + 'px'; | |
| document.body.appendChild(ripple); | |
| setTimeout(() => ripple.remove(), 1000); | |
| }); |