Spaces:
Running
Running
| // Shared JavaScript across all pages | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Initialize Three.js scene for the hero background | |
| initNetworkBackground(); | |
| // 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) { | |
| window.scrollTo({ | |
| top: target.offsetTop, | |
| behavior: 'smooth' | |
| }); | |
| } | |
| }); | |
| }); | |
| }); | |
| function initNetworkBackground() { | |
| // Get the container element | |
| const container = document.getElementById('network-background'); | |
| // Set up Three.js scene | |
| const scene = new THREE.Scene(); | |
| const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
| const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| renderer.setPixelRatio(window.devicePixelRatio); | |
| container.appendChild(renderer.domElement); | |
| // Create particle system for the network | |
| const particles = 2000; | |
| const geometry = new THREE.BufferGeometry(); | |
| const positions = new Float32Array(particles * 3); | |
| const colors = new Float32Array(particles * 3); | |
| // Create node positions | |
| for (let i = 0; i < particles * 3; i += 3) { | |
| positions[i] = (Math.random() - 0.5) * 200; | |
| positions[i + 1] = (Math.random() - 0.5) * 200; | |
| positions[i + 2] = (Math.random() - 0.5) * 200; | |
| } | |
| // Create node colors (purple spectrum) | |
| for (let i = 0; i < particles * 3; i += 3) { | |
| colors[i] = Math.random() * 0.5 + 0.5; // R | |
| colors[i + 1] = Math.random() * 0.3; // G | |
| colors[i + 2] = Math.random() * 0.8 + 0.2; // B | |
| } | |
| geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); | |
| geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); | |
| // Create material with custom shader for glow effect | |
| const material = new THREE.PointsMaterial({ | |
| size: 2, | |
| vertexColors: true, | |
| transparent: true, | |
| opacity: 0.8, | |
| blending: THREE.AdditiveBlending | |
| }); | |
| const points = new THREE.Points(geometry, material); | |
| scene.add(points); | |
| // Create connecting lines | |
| const lineGeometry = new THREE.BufferGeometry(); | |
| const linePositions = []; | |
| const lineColors = []; | |
| // Connect nearby nodes | |
| for (let i = 0; i < particles; i++) { | |
| for (let j = i + 1; j < particles; j++) { | |
| const dx = positions[i * 3] - positions[j * 3]; | |
| const dy = positions[i * 3 + 1] - positions[j * 3 + 1]; | |
| const dz = positions[i * 3 + 2] - positions[j * 3 + 2]; | |
| const distance = Math.sqrt(dx * dx + dy * dy + dz * dz); | |
| // Only connect relatively close nodes | |
| if (distance < 20) { | |
| linePositions.push( | |
| positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2], | |
| positions[j * 3], positions[j * 3 + 1], positions[j * 3 + 2] | |
| ); | |
| // Line color based on node colors | |
| lineColors.push( | |
| colors[i * 3], colors[i * 3 + 1], colors[i * 3 + 2], | |
| colors[j * 3], colors[j * 3 + 1], colors[j * 3 + 2] | |
| ); | |
| } | |
| } | |
| } | |
| lineGeometry.setAttribute('position', new THREE.Float32BufferAttribute(linePositions, 3)); | |
| lineGeometry.setAttribute('color', new THREE.Float32BufferAttribute(lineColors, 3)); | |
| const lineMaterial = new THREE.LineBasicMaterial({ | |
| vertexColors: true, | |
| transparent: true, | |
| opacity: 0.3, | |
| blending: THREE.AdditiveBlending | |
| }); | |
| const lines = new THREE.LineSegments(lineGeometry, lineMaterial); | |
| scene.add(lines); | |
| // Position camera | |
| camera.position.z = 50; | |
| // Add subtle ambient light | |
| const ambientLight = new THREE.AmbientLight(0x6a00b8, 0.2); | |
| scene.add(ambientLight); | |
| // Add directional light for highlights | |
| const directionalLight = new THREE.DirectionalLight(0xb366ff, 0.5); | |
| directionalLight.position.set(1, 1, 1); | |
| scene.add(directionalLight); | |
| // Handle window resize | |
| window.addEventListener('resize', () => { | |
| camera.aspect = window.innerWidth / window.innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| }); | |
| // Mouse movement effect | |
| let mouseX = 0; | |
| let mouseY = 0; | |
| document.addEventListener('mousemove', (event) => { | |
| mouseX = (event.clientX / window.innerWidth) * 2 - 1; | |
| mouseY = -(event.clientY / window.innerHeight) * 2 + 1; | |
| }); | |
| // Animation variables | |
| let time = 0; | |
| // Animation loop | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| time += 0.01; | |
| // Rotate the entire scene slowly | |
| points.rotation.x = time * 0.05; | |
| points.rotation.y = time * 0.03; | |
| lines.rotation.x = time * 0.05; | |
| lines.rotation.y = time * 0.03; | |
| // Apply mouse parallax effect | |
| camera.position.x += (mouseX * 5 - camera.position.x) * 0.05; | |
| camera.position.y += (mouseY * 5 - camera.position.y) * 0.05; | |
| camera.lookAt(scene.position); | |
| // Pulsing effect for nodes | |
| const positions = points.geometry.attributes.position.array; | |
| for (let i = 0; i < positions.length; i += 3) { | |
| positions[i + 1] += Math.sin(time + i * 0.01) * 0.02; | |
| } | |
| points.geometry.attributes.position.needsUpdate = true; | |
| renderer.render(scene, camera); | |
| } | |
| animate(); | |
| } |