diff --git a/frontend/src/components/ParticleBackground.jsx b/frontend/src/components/ParticleBackground.jsx index 3ac7175..6bb17a1 100644 --- a/frontend/src/components/ParticleBackground.jsx +++ b/frontend/src/components/ParticleBackground.jsx @@ -18,6 +18,8 @@ const ParticleBackground = () => { const particles = []; const particleCount = 100; + const meteors = []; + const meteorCount = 8; class Particle { constructor() { @@ -45,13 +47,93 @@ const ParticleBackground = () => { } } + class Meteor { + constructor() { + this.reset(); + } + + reset() { + this.x = Math.random() * canvas.width * 1.5; // Start further right + this.y = Math.random() * -canvas.height; // Start further above + this.vx = -(Math.random() * 5 + 5); // Faster + this.vy = Math.random() * 5 + 5; // Faster + this.len = Math.random() * 150 + 150; // Longer trail + this.color = Math.random() > 0.5 ? 'rgba(0, 185, 107, ' : 'rgba(0, 240, 255, '; + this.opacity = 0; + this.maxOpacity = Math.random() * 0.5 + 0.2; + this.wait = Math.random() * 300; // Random delay before showing up + } + + update() { + if (this.wait > 0) { + this.wait--; + return; + } + + this.x += this.vx; + this.y += this.vy; + + if (this.opacity < this.maxOpacity) { + this.opacity += 0.02; + } + + if (this.x < -this.len || this.y > canvas.height + this.len) { + this.reset(); + } + } + + draw() { + if (this.wait > 0) return; + + const tailX = this.x - this.vx * (this.len / 15); + const tailY = this.y - this.vy * (this.len / 15); + + const gradient = ctx.createLinearGradient(this.x, this.y, tailX, tailY); + gradient.addColorStop(0, this.color + this.opacity + ')'); + gradient.addColorStop(0.1, this.color + (this.opacity * 0.5) + ')'); + gradient.addColorStop(1, this.color + '0)'); + + ctx.save(); + + // Add glow effect + ctx.shadowBlur = 8; + ctx.shadowColor = this.color.replace('rgba', 'rgb').replace(', ', ')'); + + ctx.beginPath(); + ctx.strokeStyle = gradient; + ctx.lineWidth = 2; + ctx.lineCap = 'round'; + ctx.moveTo(this.x, this.y); + ctx.lineTo(tailX, tailY); + ctx.stroke(); + + // Add a bright head + ctx.beginPath(); + ctx.fillStyle = '#fff'; + ctx.arc(this.x, this.y, 1, 0, Math.PI * 2); + ctx.fill(); + + ctx.restore(); + } + } + for (let i = 0; i < particleCount; i++) { particles.push(new Particle()); } + for (let i = 0; i < meteorCount; i++) { + meteors.push(new Meteor()); + } + const animate = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); + // Draw meteors first (in background) + meteors.forEach(m => { + m.update(); + m.draw(); + }); + // Draw connecting lines ctx.lineWidth = 0.5; for (let i = 0; i < particleCount; i++) { diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx index ed87ca2..94253e8 100644 --- a/frontend/src/pages/Home.jsx +++ b/frontend/src/pages/Home.jsx @@ -156,9 +156,10 @@ const Home = () => {
{product.description}
-
- {product.chip_type} - {product.has_camera && Camera} +
+ {product.chip_type} + {product.has_camera && Camera} + {product.has_microphone && Mic}
¥{product.price}
diff --git a/frontend/src/pages/ProductDetail.jsx b/frontend/src/pages/ProductDetail.jsx index d5adf1b..29f3a2b 100644 --- a/frontend/src/pages/ProductDetail.jsx +++ b/frontend/src/pages/ProductDetail.jsx @@ -139,10 +139,10 @@ const ProductDetail = () => {

{product.name}

{product.description}

-
- {product.chip_type} - {product.has_camera && 高清摄像头} - {product.has_microphone && 阵列麦克风} +
+ {product.chip_type} + {product.has_camera && 高清摄像头} + {product.has_microphone && 阵列麦克风}