Spaces:
Running
Running
| // Three.js Jellyfish Animation | |
| class JellyfishScene { | |
| constructor() { | |
| this.scene = new THREE.Scene(); | |
| this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
| this.renderer = new THREE.WebGLRenderer({ | |
| alpha: true, | |
| antialias: true | |
| }); | |
| this.jellyfishes = []; | |
| this.init(); | |
| } | |
| init() { | |
| // Setup renderer | |
| this.renderer.setSize(window.innerWidth, window.innerHeight); | |
| this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); | |
| document.getElementById('jellyfish-canvas').appendChild(this.renderer.domElement); | |
| // Camera position | |
| this.camera.position.z = 15; | |
| // Add ambient light | |
| const ambientLight = new THREE.AmbientLight(0x222222); | |
| this.scene.add(ambientLight); | |
| // Add directional lights for glow effect | |
| const cyanLight = new THREE.PointLight(0x06b6d4, 2, 50); | |
| cyanLight.position.set(10, 10, 10); | |
| this.scene.add(cyanLight); | |
| const purpleLight = new THREE.PointLight(0x9333ea, 2, 50); | |
| purpleLight.position.set(-10, -10, 10); | |
| this.scene.add(purpleLight); | |
| // Create jellyfishes | |
| this.createJellyfishes(8); | |
| // Handle window resize | |
| window.addEventListener('resize', () => this.onWindowResize()); | |
| // Start animation | |
| this.animate(); | |
| } | |
| createJellyfishes(count) { | |
| for (let i = 0; i < count; i++) { | |
| const jellyfish = this.createJellyfish(); | |
| jellyfish.position.x = (Math.random() - 0.5) * 30; | |
| jellyfish.position.y = (Math.random() - 0.5) * 20; | |
| jellyfish.position.z = (Math.random() - 0.5) * 20; | |
| this.jellyfishes.push(jellyfish); | |
| this.scene.add(jellyfish); | |
| } | |
| } | |
| createJellyfish() { | |
| const group = new THREE.Group(); | |
| // Bell (main body) | |
| const bellGeometry = new THREE.SphereGeometry(0.8, 32, 16, 0, Math.PI * 2, 0, Math.PI / 2); | |
| const bellMaterial = new THREE.MeshPhongMaterial({ | |
| color: 0x06b6d4, | |
| transparent: true, | |
| opacity: 0.3, | |
| shininess: 100 | |
| }); | |
| const bell = new THREE.Mesh(bellGeometry, bellMaterial); | |
| group.add(bell); | |
| // Tentacles | |
| const tentacleCount = 8; | |
| for (let i = 0; i < tentacleCount; i++) { | |
| const angle = (i / tentacleCount) * Math.PI * 2; | |
| const tentacle = this.createTentacle(); | |
| tentacle.position.y = -0.8; | |
| tentacle.rotation.x = Math.PI / 4; | |
| tentacle.rotation.z = angle; | |
| group.add(tentacle); | |
| } | |
| // Glow effect | |
| const glowGeometry = new THREE.SphereGeometry(1.2, 32, 16, 0, Math.PI * 2, 0, Math.PI / 2); | |
| const glowMaterial = new THREE.MeshBasicMaterial({ | |
| color: 0x9333ea, | |
| transparent: true, | |
| opacity: 0.1 | |
| }); | |
| const glow = new THREE.Mesh(glowGeometry, glowMaterial); | |
| group.add(glow); | |
| return group; | |
| } | |
| createTentacle() { | |
| const points = []; | |
| for (let i = 0; i <= 10; i++) { | |
| points.push(new THREE.Vector3(0, -i * 0.3, 0)); | |
| } | |
| const curve = new THREE.CatmullRomCurve3(points); | |
| const geometry = new THREE.TubeGeometry(curve, 20, 0.02, 8, false); | |
| const material = new THREE.MeshPhongMaterial({ | |
| color: 0x06b6d4, | |
| transparent: true, | |
| opacity: 0.6 | |
| }); | |
| return new THREE.Mesh(geometry, material); | |
| } | |
| onWindowResize() { | |
| this.camera.aspect = window.innerWidth / window.innerHeight; | |
| this.camera.updateProjectionMatrix(); | |
| this.renderer.setSize(window.innerWidth, window.innerHeight); | |
| } | |
| animate() { | |
| requestAnimationFrame(() => this.animate()); | |
| // Animate jellyfishes | |
| this.jellyfishes.forEach((jellyfish, index) => { | |
| const time = Date.now() * 0.001; | |
| const floatSpeed = 0.5 + index * 0.1; | |
| // Floating motion | |
| jellyfish.position.y += Math.sin(time + index) * 0.005; | |
| jellyfish.position.x += Math.cos(time + index * 0.5) * 0.003; | |
| // Gentle rotation | |
| jellyfish.rotation.y += 0.001; | |
| jellyfish.rotation.x += Math.sin(time + index) * 0.0005; | |
| // Tentacle movement | |
| jellyfish.children.forEach((child, childIndex) => { | |
| if (childIndex > 0) { // Skip the bell | |
| child.rotation.x = Math.PI / 4 + Math.sin(time + childIndex) * 0.1; | |
| }); | |
| }); | |
| this.renderer.render(this.scene, this.camera); | |
| } | |
| } | |
| // Initialize when DOM is loaded | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Initialize Three.js scene | |
| new JellyfishScene(); | |
| // Add scroll 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); | |
| // Observe all glass cards | |
| document.querySelectorAll('.glass-card').forEach(card => { | |
| observer.observe(card); | |
| }); | |
| }); | |
| // 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' | |
| }); | |
| } | |
| }); | |
| }); |