// Creative Studio Animation for Nexus class CreativeParticleAnimation { constructor(canvas) { this.canvas = canvas; this.ctx = canvas.getContext('2d'); this.particles = []; this.flowField = []; this.mouse = { x: 0, y: 0, radius: 200 }; this.time = 0; this.init(); this.animate(); this.setupEventListeners(); } init() { this.resizeCanvas(); this.createFlowField(); // Create creative particles with artistic properties const particleCount = Math.min(80, Math.floor(window.innerWidth / 15)); for (let i = 0; i < particleCount; i++) { this.particles.push({ x: Math.random() * this.canvas.width, y: Math.random() * this.canvas.height, vx: (Math.random() - 0.5) * 3, vy: (Math.random() - 0.5) * 3, radius: Math.random() * 4 + 1, hue: Math.random() * 360, saturation: 70 + Math.random() * 30, lightness: 50 + Math.random() * 30, life: Math.random() * 100, maxLife: 100 + Math.random() * 50 }); } } createFlowField() { const cols = Math.floor(this.canvas.width / 50); const rows = Math.floor(this.canvas.height / 50); for (let x = 0; x < cols; x++) { this.flowField[x] = []; for (let y = 0; y < rows; y++) { const angle = Math.sin(x * 0.01 + this.time * 0.001) * Math.cos(y * 0.01 + this.time * 0.001); this.flowField[x][y] = { angle: angle, strength: Math.sin(angle) * 2 }; } } } resizeCanvas() { this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; } setupEventListeners() { window.addEventListener('resize', () => this.resizeCanvas()); this.canvas.addEventListener('mousemove', (e) => { this.mouse.x = e.clientX; this.mouse.y = e.clientY; }); this.canvas.addEventListener('mouseleave', () => { this.mouse.x = undefined; this.mouse.y = undefined; }); } animate() { requestAnimationFrame(() => this.animate()); // Create trailing effect this.ctx.fillStyle = 'rgba(17, 24, 39, 0.1)'; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); this.time += 1; this.updateParticles(); this.drawParticleTrails(); this.drawConnections(); this.drawArtisticElements(); } updateParticles() { this.particles.forEach((particle, index) => { // Flow field influence const col = Math.floor(particle.x / 50); const row = Math.floor(particle.y / 50); if (this.flowField[col] && this.flowField[col][row]) { const field = this.flowField[col][row]; particle.vx += Math.cos(field.angle) * field.strength * 0.1; particle.vy += Math.sin(field.angle) * field.strength * 0.1; } // Mouse interaction - attract particles if (this.mouse.x && this.mouse.y) { const dx = this.mouse.x - particle.x; const dy = this.mouse.y - particle.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < this.mouse.radius) { const force = (this.mouse.radius - distance) / this.mouse.radius; const angle = Math.atan2(dy, dx); particle.vx += Math.cos(angle) * force * 0.8; particle.vy += Math.sin(angle) * force * 0.8; // Color shift on interaction particle.hue = (particle.hue + force * 20) % 360; } } // Natural movement particle.x += particle.vx; particle.y += particle.vy; particle.vx *= 0.98; particle.vy *= 0.98; // Boundary bounce with creative wrapping if (particle.x < 0 || particle.x > this.canvas.width) particle.vx *= -0.8; if (particle.y < 0 || particle.y > this.canvas.height) particle.vy *= -0.8; // Keep particles in bounds particle.x = Math.max(0, Math.min(this.canvas.width, particle.x)); particle.y = Math.max(0, Math.min(this.canvas.height, particle.y)); // Particle life cycle particle.life += 1; if (particle.life > particle.maxLife) { // Respawn with new properties particle.x = Math.random() * this.canvas.width; particle.y = Math.random() * this.canvas.height; particle.life = 0; particle.hue = Math.random() * 360; } }); } drawParticleTrails() { this.particles.forEach(particle => { // Draw particle trail this.ctx.beginPath(); this.ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2); const alpha = (particle.maxLife - particle.life) / particle.maxLife; const color = `hsla(${particle.hue}, ${particle.saturation}%, ${particle.lightness}%, ${alpha})`; this.ctx.fillStyle = color; this.ctx.fill(); // Add glow effect this.ctx.shadowColor = color; this.ctx.shadowBlur = 10; this.ctx.fill(); this.ctx.shadowBlur = 0; }); } drawConnections() { for (let i = 0; i < this.particles.length; i++) { for (let j = i + 1; j < this.particles.length; j++) { const dx = this.particles[i].x - this.particles[j].x; const dy = this.particles[i].y - this.particles[j].y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < 120) { const opacity = 1 - (distance / 120); const hue = (this.particles[i].hue + this.particles[j].hue) / 2; this.ctx.strokeStyle = `hsla(${hue}, 80%, 60%, ${opacity * 0.3})`; this.ctx.lineWidth = 1; this.ctx.beginPath(); this.ctx.moveTo(this.particles[i].x, this.particles[i].y); this.ctx.lineTo(this.particles[j].x, this.particles[j].y); this.ctx.stroke(); } } } } drawArtisticElements() { // Draw abstract geometric shapes if (this.time % 300 === 0) { this.drawGeometricShape(); } } drawGeometricShape() { const centerX = this.canvas.width / 2; const centerY = this.canvas.height / 2; const size = 50 + Math.sin(this.time * 0.01) * 30; this.ctx.save(); this.ctx.translate(centerX, centerY); this.ctx.rotate(this.time * 0.001); // Draw rotating polygon this.ctx.beginPath(); for (let i = 0; i < 6; i++) { const angle = (i / 6) * Math.PI * 2; const x = Math.cos(angle) * size; const y = Math.sin(angle) * size; if (i === 0) this.ctx.moveTo(x, y); else this.ctx.lineTo(x, y); } this.ctx.closePath(); this.ctx.strokeStyle = `hsla(${(this.time * 0.5) % 360}, 70%, 70%, 0.5)`; this.ctx.lineWidth = 2; this.ctx.stroke(); this.ctx.restore(); } } // Initialize when DOM is loaded document.addEventListener('DOMContentLoaded', function() { const canvas = document.getElementById('networkCanvas'); if (canvas) { new CreativeParticleAnimation(canvas); } // Add interactive gallery effects addGalleryInteractions(); }); // Gallery interaction effects function addGalleryInteractions() { const galleryItems = document.querySelectorAll('.grid > div'); galleryItems.forEach((item, index) => { item.addEventListener('mouseenter', function() { this.style.transform = 'scale(1.05) rotate(2deg)'; this.style.zIndex = '10'; }); item.addEventListener('mouseleave', function() { this.style.transform = 'scale(1) rotate(0deg)'; this.style.zIndex = '1'; }); // Add click effect item.addEventListener('click', function() { this.classList.add('animate-pulse'); setTimeout(() => { this.classList.remove('animate-pulse'); }, 1000); }); }); } // Smooth scrolling 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' }); } }); }); // Add scroll-based animations window.addEventListener('scroll', function() { const scrolled = window.pageYOffset; const parallax = document.querySelector('#networkCanvas'); if (parallax) { parallax.style.transform = `translateY(${scrolled * 0.5}px)`; } });