RamEx-Flow / front /src /components /ParticleEffect.vue
Aye10032
feat: Fix frontend null safety and remove dead code
1862557
Raw
History Blame Contribute Delete
3.28 kB
<template>
<div class="particle-container" ref="container">
<canvas ref="canvas" class="particle-canvas"></canvas>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
// 定义组件名称
defineOptions({
name: 'ParticleEffect'
})
const container = ref<HTMLElement>()
const canvas = ref<HTMLCanvasElement>()
let animationId: number
let particles: Particle[] = []
interface Particle {
x: number
y: number
vx: number
vy: number
size: number
opacity: number
color: string
}
const colors = [
'rgba(26, 26, 26, 0.12)',
'rgba(26, 26, 26, 0.08)',
'rgba(92, 92, 92, 0.1)',
'rgba(138, 138, 138, 0.08)',
'rgba(26, 26, 26, 0.06)'
]
const createParticle = (): Particle => {
return {
x: Math.random() * (canvas.value?.width || 0),
y: Math.random() * (canvas.value?.height || 0),
vx: (Math.random() - 0.5) * 2,
vy: (Math.random() - 0.5) * 2,
size: Math.random() * 3 + 1,
opacity: Math.random() * 0.5 + 0.2,
color: colors[Math.floor(Math.random() * colors.length)] ?? colors[0]!
}
}
const initParticles = () => {
particles = []
for (let i = 0; i < 50; i++) {
particles.push(createParticle())
}
}
const updateParticles = () => {
if (!canvas.value) return
const ctx = canvas.value.getContext('2d')
if (!ctx) return
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height)
particles.forEach((particle, index) => {
// 更新位置
particle.x += particle.vx
particle.y += particle.vy
// 边界检测
if (particle.x < 0 || particle.x > canvas.value!.width) {
particle.vx *= -1
}
if (particle.y < 0 || particle.y > canvas.value!.height) {
particle.vy *= -1
}
// 绘制粒子
ctx.beginPath()
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2)
ctx.fillStyle = particle.color.replace(/[\d.]+\)$/g, `${particle.opacity})`)
ctx.fill()
// 绘制连线
particles.slice(index + 1).forEach(otherParticle => {
const dx = particle.x - otherParticle.x
const dy = particle.y - otherParticle.y
const distance = Math.sqrt(dx * dx + dy * dy)
if (distance < 100) {
ctx.beginPath()
ctx.moveTo(particle.x, particle.y)
ctx.lineTo(otherParticle.x, otherParticle.y)
ctx.strokeStyle = `rgba(102, 126, 234, ${0.1 * (1 - distance / 100)})`
ctx.lineWidth = 1
ctx.stroke()
}
})
})
animationId = requestAnimationFrame(updateParticles)
}
const resizeCanvas = () => {
if (!canvas.value || !container.value) return
const rect = container.value.getBoundingClientRect()
canvas.value.width = rect.width
canvas.value.height = rect.height
}
onMounted(() => {
if (!canvas.value || !container.value) return
resizeCanvas()
initParticles()
updateParticles()
window.addEventListener('resize', resizeCanvas)
})
onUnmounted(() => {
if (animationId) {
cancelAnimationFrame(animationId)
}
window.removeEventListener('resize', resizeCanvas)
})
</script>
<style scoped>
.particle-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 0;
}
.particle-canvas {
width: 100%;
height: 100%;
}
</style>