feat: 在 ParticleBackground 组件中添加粒子初始化延迟和渐进式显示功能,优化粒子透明度处理,提升动画效果和用户体验。

This commit is contained in:
Cat Tom 2025-03-26 12:49:02 +08:00
parent a1e5bfd74d
commit 112bc980b5

View File

@ -27,10 +27,16 @@ const config = {
particleCount: isMobile.value ? 800 : 1500, particleCount: isMobile.value ? 800 : 1500,
particleOpacity: 0.6, particleOpacity: 0.6,
lineOpacity: 0.15, lineOpacity: 0.15,
glowSize: 3 glowSize: 3,
initDelay: 500, //
initDuration: 2000, //
batchSize: 50 //
} }
let scene, camera, renderer, particles, lines, mouse = { x: 0, y: 0 } let scene, camera, renderer, particles, lines, mouse = { x: 0, y: 0 }
let initializedParticles = 0
let isInitializing = false
let initStartTime = 0
const initThreeJS = () => { const initThreeJS = () => {
if (!canvasRef.value) return if (!canvasRef.value) return
@ -71,21 +77,26 @@ const createParticles = () => {
const positions = new Float32Array(config.particleCount * 3) const positions = new Float32Array(config.particleCount * 3)
const colors = new Float32Array(config.particleCount * 3) const colors = new Float32Array(config.particleCount * 3)
const sizes = new Float32Array(config.particleCount) const sizes = new Float32Array(config.particleCount)
const opacities = new Float32Array(config.particleCount)
//
for (let i = 0; i < config.particleCount; i++) {
opacities[i] = 0
}
//
for (let i = 0; i < config.particleCount; i++) { for (let i = 0; i < config.particleCount; i++) {
const i3 = i * 3 const i3 = i * 3
// 使
const radius = (Math.random() * 0.8 + 0.2) * config.systemRadius const radius = (Math.random() * 0.8 + 0.2) * config.systemRadius
const theta = Math.random() * Math.PI * 2 const theta = Math.random() * Math.PI * 2
const phi = Math.acos(2 * Math.random() - 1) const phi = Math.acos(2 * Math.random() - 1)
const spiral = Math.sin(theta * 3) * 2 // const spiral = Math.sin(theta * 3) * 2
positions[i3] = radius * Math.sin(phi) * Math.cos(theta) + spiral positions[i3] = radius * Math.sin(phi) * Math.cos(theta) + spiral
positions[i3 + 1] = radius * Math.sin(phi) * Math.sin(theta) + spiral positions[i3 + 1] = radius * Math.sin(phi) * Math.sin(theta) + spiral
positions[i3 + 2] = radius * Math.cos(phi) positions[i3 + 2] = radius * Math.cos(phi)
//
const color = new THREE.Color( const color = new THREE.Color(
config.colorVariation[Math.floor(Math.random() * config.colorVariation.length)] config.colorVariation[Math.floor(Math.random() * config.colorVariation.length)]
) )
@ -93,15 +104,14 @@ const createParticles = () => {
colors[i3 + 1] = color.g colors[i3 + 1] = color.g
colors[i3 + 2] = color.b colors[i3 + 2] = color.b
//
sizes[i] = config.particleSize * (0.5 + Math.random() * 0.8) sizes[i] = config.particleSize * (0.5 + Math.random() * 0.8)
} }
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)) geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3))
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1)) geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1))
geometry.setAttribute('opacity', new THREE.BufferAttribute(opacities, 1))
// 使
const material = new THREE.ShaderMaterial({ const material = new THREE.ShaderMaterial({
uniforms: { uniforms: {
time: { value: 0 }, time: { value: 0 },
@ -110,11 +120,13 @@ const createParticles = () => {
vertexShader: ` vertexShader: `
uniform float time; uniform float time;
attribute float size; attribute float size;
attribute float opacity;
varying vec3 vColor; varying vec3 vColor;
varying float vOpacity;
void main() { void main() {
vColor = color; vColor = color;
vOpacity = opacity;
vec3 pos = position; vec3 pos = position;
//
pos.y += sin(time * 0.5 + position.x * 0.5) * 0.5; pos.y += sin(time * 0.5 + position.x * 0.5) * 0.5;
pos.x += cos(time * 0.3 + position.y * 0.5) * 0.3; pos.x += cos(time * 0.3 + position.y * 0.5) * 0.3;
vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0); vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
@ -124,12 +136,12 @@ const createParticles = () => {
`, `,
fragmentShader: ` fragmentShader: `
varying vec3 vColor; varying vec3 vColor;
varying float vOpacity;
void main() { void main() {
//
vec2 center = gl_PointCoord - vec2(0.5); vec2 center = gl_PointCoord - vec2(0.5);
float dist = length(center); float dist = length(center);
float alpha = 1.0 - smoothstep(0.3, 0.5, dist); float alpha = 1.0 - smoothstep(0.3, 0.5, dist);
gl_FragColor = vec4(vColor, alpha * 0.8); gl_FragColor = vec4(vColor, alpha * vOpacity);
} }
`, `,
transparent: true, transparent: true,
@ -141,8 +153,11 @@ const createParticles = () => {
particles = new THREE.Points(geometry, material) particles = new THREE.Points(geometry, material)
scene.add(particles) scene.add(particles)
// 线 //
createConnections(positions, colors) setTimeout(() => {
isInitializing = true
initStartTime = Date.now()
}, config.initDelay)
} }
const createConnections = (positions, colors) => { const createConnections = (positions, colors) => {
@ -216,16 +231,41 @@ const updateParticles = (time) => {
if (!particles || !particles.geometry) return if (!particles || !particles.geometry) return
const positions = particles.geometry.attributes.position.array const positions = particles.geometry.attributes.position.array
const opacities = particles.geometry.attributes.opacity.array
//
if (isInitializing) {
const elapsed = Date.now() - initStartTime
const progress = Math.min(elapsed / config.initDuration, 1)
//
const targetCount = Math.floor(config.particleCount * progress)
//
while (initializedParticles < targetCount) {
const batchSize = Math.min(config.batchSize, targetCount - initializedParticles)
for (let i = initializedParticles; i < initializedParticles + batchSize; i++) {
opacities[i] = config.particleOpacity
}
initializedParticles += batchSize
}
//
if (progress >= 1) {
isInitializing = false
}
particles.geometry.attributes.opacity.needsUpdate = true
}
//
for (let i = 0; i < config.particleCount; i++) { for (let i = 0; i < config.particleCount; i++) {
const i3 = i * 3 const i3 = i * 3
//
positions[i3] += Math.sin(time + i * 0.05) * 0.01 positions[i3] += Math.sin(time + i * 0.05) * 0.01
positions[i3 + 1] += Math.cos(time + i * 0.03) * 0.01 positions[i3 + 1] += Math.cos(time + i * 0.03) * 0.01
positions[i3 + 2] += Math.sin(time * 0.3 + i * 0.04) * 0.01 positions[i3 + 2] += Math.sin(time * 0.3 + i * 0.04) * 0.01
//
if (mouse.x !== null && mouse.y !== null) { if (mouse.x !== null && mouse.y !== null) {
const dx = positions[i3] - mouse.x * 20 const dx = positions[i3] - mouse.x * 20
const dy = positions[i3 + 1] - mouse.y * 20 const dy = positions[i3 + 1] - mouse.y * 20