feat(pages): 添加联系、首页和作品页面,更新样式和配置文件
This commit is contained in:
126
src/components/ParticleBackground.tsx
Normal file
126
src/components/ParticleBackground.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
interface Particle {
|
||||
x: number
|
||||
y: number
|
||||
size: number
|
||||
speedX: number
|
||||
speedY: number
|
||||
opacity: number
|
||||
}
|
||||
|
||||
export default function ParticleBackground() {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current
|
||||
if (!canvas) return
|
||||
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) return
|
||||
|
||||
let animationId: number
|
||||
let particles: Particle[] = []
|
||||
let mouseX = 0
|
||||
let mouseY = 0
|
||||
|
||||
const resize = () => {
|
||||
canvas.width = window.innerWidth
|
||||
canvas.height = window.innerHeight
|
||||
}
|
||||
|
||||
const createParticles = () => {
|
||||
particles = []
|
||||
const count = Math.floor((canvas.width * canvas.height) / 15000)
|
||||
for (let i = 0; i < count; i++) {
|
||||
particles.push({
|
||||
x: Math.random() * canvas.width,
|
||||
y: Math.random() * canvas.height,
|
||||
size: Math.random() * 2 + 0.5,
|
||||
speedX: (Math.random() - 0.5) * 0.5,
|
||||
speedY: (Math.random() - 0.5) * 0.5,
|
||||
opacity: Math.random() * 0.5 + 0.1
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const drawParticles = () => {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
|
||||
particles.forEach((particle, i) => {
|
||||
particle.x += particle.speedX
|
||||
particle.y += particle.speedY
|
||||
|
||||
if (particle.x < 0) particle.x = canvas.width
|
||||
if (particle.x > canvas.width) particle.x = 0
|
||||
if (particle.y < 0) particle.y = canvas.height
|
||||
if (particle.y > canvas.height) particle.y = 0
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2)
|
||||
ctx.fillStyle = `rgba(102, 126, 234, ${particle.opacity})`
|
||||
ctx.fill()
|
||||
|
||||
particles.forEach((particle2, j) => {
|
||||
if (i === j) return
|
||||
const dx = particle.x - particle2.x
|
||||
const dy = particle.y - particle2.y
|
||||
const distance = Math.sqrt(dx * dx + dy * dy)
|
||||
|
||||
if (distance < 150) {
|
||||
ctx.beginPath()
|
||||
ctx.strokeStyle = `rgba(102, 126, 234, ${0.15 * (1 - distance / 150)})`
|
||||
ctx.lineWidth = 0.5
|
||||
ctx.moveTo(particle.x, particle.y)
|
||||
ctx.lineTo(particle2.x, particle2.y)
|
||||
ctx.stroke()
|
||||
}
|
||||
})
|
||||
|
||||
const dx = particle.x - mouseX
|
||||
const dy = particle.y - mouseY
|
||||
const distance = Math.sqrt(dx * dx + dy * dy)
|
||||
|
||||
if (distance < 200) {
|
||||
const force = (200 - distance) / 200
|
||||
particle.speedX += (dx / distance) * force * 0.02
|
||||
particle.speedY += (dy / distance) * force * 0.02
|
||||
}
|
||||
|
||||
particle.speedX *= 0.99
|
||||
particle.speedY *= 0.99
|
||||
})
|
||||
|
||||
animationId = requestAnimationFrame(drawParticles)
|
||||
}
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
mouseX = e.clientX
|
||||
mouseY = e.clientY
|
||||
}
|
||||
|
||||
resize()
|
||||
createParticles()
|
||||
drawParticles()
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
resize()
|
||||
createParticles()
|
||||
})
|
||||
window.addEventListener('mousemove', handleMouseMove)
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(animationId)
|
||||
window.removeEventListener('resize', resize)
|
||||
window.removeEventListener('mousemove', handleMouseMove)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
style={{ zIndex: 1 }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user