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;