added moving star and meteorites
This commit is contained in:
		
							parent
							
								
									2e961232f1
								
							
						
					
					
						commit
						fd57d47956
					
				
					 5 changed files with 480 additions and 25 deletions
				
			
		
							
								
								
									
										319
									
								
								src/js/animation/Meteor.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										319
									
								
								src/js/animation/Meteor.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,319 @@ | |||
| class Meteor { | ||||
|   constructor(container) { | ||||
|     this.element = document.createElement("div"); | ||||
|     this.element.style.position = "absolute"; | ||||
|     this.container = container; | ||||
|     this.speed = Math.random() * 3 + 4; | ||||
|     this.life = 1.0; | ||||
|     this.particles = []; | ||||
| 
 | ||||
|     // Enhanced color palette
 | ||||
|     this.colorType = Math.floor(Math.random() * 4); | ||||
|     this.colors = this.getMeteorColors(); | ||||
| 
 | ||||
|     // Get container dimensions for pixel-based positioning
 | ||||
|     this.containerRect = container.getBoundingClientRect(); | ||||
| 
 | ||||
|     // Start from random edge with PIXEL-based positioning
 | ||||
|     const edge = Math.floor(Math.random() * 4); | ||||
|     if (edge === 0) { | ||||
|       // Top
 | ||||
|       this.x = Math.random() * this.containerRect.width; | ||||
|       this.y = -20; | ||||
|     } else if (edge === 1) { | ||||
|       // Right
 | ||||
|       this.x = this.containerRect.width + 20; | ||||
|       this.y = Math.random() * this.containerRect.height; | ||||
|     } else if (edge === 2) { | ||||
|       // Bottom
 | ||||
|       this.x = Math.random() * this.containerRect.width; | ||||
|       this.y = this.containerRect.height + 20; | ||||
|     } else { | ||||
|       // Left
 | ||||
|       this.x = -20; | ||||
|       this.y = Math.random() * this.containerRect.height; | ||||
|     } | ||||
| 
 | ||||
|     // More dynamic movement patterns
 | ||||
|     const centerX = this.containerRect.width / 2 + (Math.random() - 0.5) * 200; | ||||
|     const centerY = this.containerRect.height / 2 + (Math.random() - 0.5) * 200; | ||||
|     this.angle = | ||||
|       Math.atan2(centerY - this.y, centerX - this.x) + | ||||
|       (Math.random() - 0.5) * 0.8; | ||||
|     this.dx = Math.cos(this.angle) * this.speed; | ||||
|     this.dy = Math.sin(this.angle) * this.speed; | ||||
| 
 | ||||
|     this.init(); | ||||
|   } | ||||
| 
 | ||||
|   getMeteorColors() { | ||||
|     const colorSchemes = [ | ||||
|       { | ||||
|         head: "rgba(255, 255, 255, 1)", | ||||
|         core: "rgba(255, 230, 150, 0.9)", | ||||
|         mid: "rgba(255, 180, 80, 0.7)", | ||||
|         outer: "rgba(255, 100, 50, 0.5)", | ||||
|         glow: "rgba(255, 200, 100, 0.9)", | ||||
|       }, | ||||
|       { | ||||
|         head: "rgba(255, 255, 255, 1)", | ||||
|         core: "rgba(180, 220, 255, 0.9)", | ||||
|         mid: "rgba(100, 180, 255, 0.7)", | ||||
|         outer: "rgba(50, 100, 255, 0.5)", | ||||
|         glow: "rgba(100, 180, 255, 0.9)", | ||||
|       }, | ||||
|       { | ||||
|         head: "rgba(255, 255, 255, 1)", | ||||
|         core: "rgba(220, 180, 255, 0.9)", | ||||
|         mid: "rgba(200, 100, 255, 0.7)", | ||||
|         outer: "rgba(180, 50, 255, 0.5)", | ||||
|         glow: "rgba(200, 150, 255, 0.9)", | ||||
|       }, | ||||
|       { | ||||
|         head: "rgba(255, 255, 255, 1)", | ||||
|         core: "rgba(180, 255, 200, 0.9)", | ||||
|         mid: "rgba(80, 255, 150, 0.7)", | ||||
|         outer: "rgba(50, 200, 100, 0.5)", | ||||
|         glow: "rgba(150, 255, 180, 0.9)", | ||||
|       }, | ||||
|     ]; | ||||
|     return colorSchemes[this.colorType]; | ||||
|   } | ||||
| 
 | ||||
|   init() { | ||||
|     const size = Math.random() * 4 + 10; | ||||
|     const colors = this.colors; | ||||
| 
 | ||||
|     // Create enhanced meteor head with multiple layers
 | ||||
|     const head = document.createElement("div"); | ||||
|     head.style.width = `${size}px`; | ||||
|     head.style.height = `${size}px`; | ||||
|     head.style.borderRadius = "50%"; | ||||
|     head.style.background = ` | ||||
|       radial-gradient( | ||||
|         circle at 30% 30%, | ||||
|         ${colors.head} 0%, | ||||
|         ${colors.core} 30%, | ||||
|         ${colors.mid} 60%, | ||||
|         ${colors.outer} 100% | ||||
|       ) | ||||
|     `;
 | ||||
|     head.style.boxShadow = ` | ||||
|       0 0 ${size * 4}px ${colors.glow}, | ||||
|       0 0 ${size * 2}px rgba(255, 255, 255, 0.8), | ||||
|       inset 0 0 ${size * 0.5}px rgba(255, 255, 255, 0.9) | ||||
|     `;
 | ||||
|     head.style.position = "absolute"; | ||||
|     head.style.left = "50%"; | ||||
|     head.style.top = "50%"; | ||||
|     head.style.transform = "translate(-50%, -50%)"; | ||||
|     head.style.zIndex = "3"; | ||||
|     head.style.filter = "blur(0.5px)"; | ||||
| 
 | ||||
|     // Create multi-layer tail
 | ||||
|     this.createTailLayer(size * 3, size * 1.2, colors.core, 1, "1px"); | ||||
|     this.createTailLayer(size * 5, size * 2, colors.mid, 0.6, "2px"); | ||||
|     this.createTailLayer(size * 8, size * 3, colors.outer, 0.3, "3px"); | ||||
| 
 | ||||
|     // Assemble meteor
 | ||||
|     this.element.appendChild(head); | ||||
|     this.element.style.left = `${this.x}px`; | ||||
|     this.element.style.top = `${this.y}px`; | ||||
|     this.element.style.zIndex = "10"; | ||||
|     this.element.style.filter = "blur(0.5px)"; | ||||
| 
 | ||||
|     this.head = head; | ||||
|     this.size = size; | ||||
| 
 | ||||
|     this.container.appendChild(this.element); | ||||
| 
 | ||||
|     // Add trail particles
 | ||||
|     this.createTrailParticles(); | ||||
|   } | ||||
| 
 | ||||
|   createTailLayer(length, width, color, opacity, blur) { | ||||
|     const tail = document.createElement("div"); | ||||
|     const tailAngle = Math.atan2(this.dy, this.dx) + Math.PI; | ||||
| 
 | ||||
|     tail.style.width = `${length}px`; | ||||
|     tail.style.height = `${width}px`; | ||||
|     tail.style.position = "absolute"; | ||||
|     tail.style.left = "50%"; | ||||
|     tail.style.top = "50%"; | ||||
|     tail.style.transformOrigin = "left center"; | ||||
|     tail.style.transform = `translate(0, -50%) rotate(${tailAngle}rad)`; | ||||
|     tail.style.background = `linear-gradient(to right, 
 | ||||
|       ${color} 0%,  | ||||
|       ${color.replace(")", ", 0.8)").replace("rgba", "rgba")} 20%, | ||||
|       ${color.replace(")", ", 0.4)").replace("rgba", "rgba")} 50%, | ||||
|       ${color.replace(")", ", 0.1)").replace("rgba", "rgba")} 80%, | ||||
|       transparent 100%)`;
 | ||||
|     tail.style.opacity = opacity; | ||||
|     tail.style.filter = `blur(${blur})`; | ||||
|     tail.style.pointerEvents = "none"; | ||||
|     tail.style.zIndex = "2"; | ||||
|     tail.style.borderRadius = "0 50% 50% 0"; | ||||
| 
 | ||||
|     this.element.appendChild(tail); | ||||
|     this.tail = tail; | ||||
|   } | ||||
| 
 | ||||
|   createTrailParticles() { | ||||
|     // Create initial trail particles
 | ||||
|     for (let i = 0; i < 5; i++) { | ||||
|       this.addTrailParticle(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   addTrailParticle() { | ||||
|     const particle = document.createElement("div"); | ||||
|     const size = Math.random() * 2 + 1; | ||||
|     const colors = this.colors; | ||||
| 
 | ||||
|     particle.style.width = `${size}px`; | ||||
|     particle.style.height = `${size}px`; | ||||
|     particle.style.borderRadius = "50%"; | ||||
|     particle.style.background = colors.mid; | ||||
|     particle.style.position = "absolute"; | ||||
|     particle.style.left = `${this.x}px`; | ||||
|     particle.style.top = `${this.y}px`; | ||||
|     particle.style.boxShadow = `0 0 ${size * 3}px ${colors.glow}`; | ||||
|     particle.style.opacity = "0.7"; | ||||
|     particle.style.zIndex = "1"; | ||||
|     particle.style.pointerEvents = "none"; | ||||
| 
 | ||||
|     this.container.appendChild(particle); | ||||
| 
 | ||||
|     this.particles.push({ | ||||
|       element: particle, | ||||
|       life: 1.0, | ||||
|       x: this.x, | ||||
|       y: this.y, | ||||
|       vx: (Math.random() - 0.5) * 0.5, | ||||
|       vy: (Math.random() - 0.5) * 0.5, | ||||
|       size: size, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   update() { | ||||
|     // Smooth pixel-based movement
 | ||||
|     this.x += this.dx; | ||||
|     this.y += this.dy; | ||||
|     this.life -= 0.005; | ||||
| 
 | ||||
|     // Use pixel positioning for smooth movement
 | ||||
|     this.element.style.left = `${this.x}px`; | ||||
|     this.element.style.top = `${this.y}px`; | ||||
|     this.element.style.opacity = this.life; | ||||
| 
 | ||||
|     // Update tail direction
 | ||||
|     const tailAngle = Math.atan2(this.dy, this.dx) + Math.PI; | ||||
|     const tails = this.element.querySelectorAll("div:not(.particle)"); | ||||
|     tails.forEach((tail) => { | ||||
|       if (tail !== this.head) { | ||||
|         tail.style.transform = `translate(0, -50%) rotate(${tailAngle}rad)`; | ||||
|         tail.style.opacity = this.life; | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     // Add new trail particles periodically
 | ||||
|     if (Math.random() < 0.3) { | ||||
|       this.addTrailParticle(); | ||||
|     } | ||||
| 
 | ||||
|     // Update existing particles with pixel positioning
 | ||||
|     for (let i = this.particles.length - 1; i >= 0; i--) { | ||||
|       const p = this.particles[i]; | ||||
|       p.life -= 0.02; | ||||
|       p.x += p.vx; | ||||
|       p.y += p.vy; | ||||
| 
 | ||||
|       p.element.style.left = `${p.x}px`; | ||||
|       p.element.style.top = `${p.y}px`; | ||||
|       p.element.style.opacity = p.life; | ||||
|       p.element.style.transform = `scale(${p.life})`; | ||||
| 
 | ||||
|       if (p.life <= 0) { | ||||
|         p.element.remove(); | ||||
|         this.particles.splice(i, 1); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // Enhanced removal condition with fading
 | ||||
|     if ( | ||||
|       this.x < -50 || | ||||
|       this.x > this.containerRect.width + 50 || | ||||
|       this.y < -50 || | ||||
|       this.y > this.containerRect.height + 50 || | ||||
|       this.life <= 0 | ||||
|     ) { | ||||
|       // Fade out particles
 | ||||
|       this.particles.forEach((p) => { | ||||
|         p.element.style.transition = "opacity 0.5s"; | ||||
|         p.element.style.opacity = "0"; | ||||
|         setTimeout(() => p.element.remove(), 500); | ||||
|       }); | ||||
|       this.remove(); | ||||
|       return false; | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   remove() { | ||||
|     // Add explosion effect when meteor dies
 | ||||
|     if (this.life > 0.3) { | ||||
|       this.createExplosion(); | ||||
|     } | ||||
| 
 | ||||
|     setTimeout(() => { | ||||
|       this.element.remove(); | ||||
|     }, 300); | ||||
|   } | ||||
| 
 | ||||
|   createExplosion() { | ||||
|     for (let i = 0; i < 8; i++) { | ||||
|       const particle = document.createElement("div"); | ||||
|       const size = Math.random() * 3 + 2; | ||||
|       const colors = this.colors; | ||||
| 
 | ||||
|       particle.style.width = `${size}px`; | ||||
|       particle.style.height = `${size}px`; | ||||
|       particle.style.borderRadius = "50%"; | ||||
|       particle.style.background = colors.core; | ||||
|       particle.style.position = "absolute"; | ||||
|       particle.style.left = `${this.x}px`; | ||||
|       particle.style.top = `${this.y}px`; | ||||
|       particle.style.boxShadow = `0 0 ${size * 4}px ${colors.glow}`; | ||||
|       particle.style.opacity = "0.8"; | ||||
|       particle.style.zIndex = "2"; | ||||
| 
 | ||||
|       this.container.appendChild(particle); | ||||
| 
 | ||||
|       // Animate explosion
 | ||||
|       const angle = (i / 8) * Math.PI * 2; | ||||
|       const distance = Math.random() * 20 + 10; | ||||
|       const duration = Math.random() * 500 + 500; | ||||
| 
 | ||||
|       particle.animate( | ||||
|         [ | ||||
|           { | ||||
|             transform: "translate(0, 0) scale(1)", | ||||
|             opacity: 0.8, | ||||
|           }, | ||||
|           { | ||||
|             transform: `translate(${Math.cos(angle) * distance}px, ${ | ||||
|               Math.sin(angle) * distance | ||||
|             }px) scale(0.1)`,
 | ||||
|             opacity: 0, | ||||
|           }, | ||||
|         ], | ||||
|         { | ||||
|           duration: duration, | ||||
|           easing: "cubic-bezier(0.4, 0, 0.2, 1)", | ||||
|         } | ||||
|       ).onfinish = () => particle.remove(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export default Meteor; | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue