diff --git a/src/components/ParticleBackground.vue b/src/components/ParticleBackground.vue index 65c8961..8dc30b6 100644 --- a/src/components/ParticleBackground.vue +++ b/src/components/ParticleBackground.vue @@ -27,10 +27,16 @@ const config = { particleCount: isMobile.value ? 800 : 1500, particleOpacity: 0.6, 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 initializedParticles = 0 +let isInitializing = false +let initStartTime = 0 const initThreeJS = () => { if (!canvasRef.value) return @@ -71,21 +77,26 @@ const createParticles = () => { const positions = new Float32Array(config.particleCount * 3) const colors = new Float32Array(config.particleCount * 3) 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++) { const i3 = i * 3 - // 使用螺旋分布而不是随机分布 const radius = (Math.random() * 0.8 + 0.2) * config.systemRadius const theta = Math.random() * Math.PI * 2 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 + 1] = radius * Math.sin(phi) * Math.sin(theta) + spiral positions[i3 + 2] = radius * Math.cos(phi) - // 随机选择颜色 const color = new THREE.Color( config.colorVariation[Math.floor(Math.random() * config.colorVariation.length)] ) @@ -93,15 +104,14 @@ const createParticles = () => { colors[i3 + 1] = color.g colors[i3 + 2] = color.b - // 变化的粒子大小 sizes[i] = config.particleSize * (0.5 + Math.random() * 0.8) } geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)) geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1)) + geometry.setAttribute('opacity', new THREE.BufferAttribute(opacities, 1)) - // 使用自定义着色器材质 const material = new THREE.ShaderMaterial({ uniforms: { time: { value: 0 }, @@ -110,11 +120,13 @@ const createParticles = () => { vertexShader: ` uniform float time; attribute float size; + attribute float opacity; varying vec3 vColor; + varying float vOpacity; void main() { vColor = color; + vOpacity = opacity; vec3 pos = position; - // 添加波浪动效 pos.y += sin(time * 0.5 + position.x * 0.5) * 0.5; pos.x += cos(time * 0.3 + position.y * 0.5) * 0.3; vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0); @@ -124,12 +136,12 @@ const createParticles = () => { `, fragmentShader: ` varying vec3 vColor; + varying float vOpacity; void main() { - // 创建柔和的光晕效果 vec2 center = gl_PointCoord - vec2(0.5); float dist = length(center); 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, @@ -141,8 +153,11 @@ const createParticles = () => { particles = new THREE.Points(geometry, material) scene.add(particles) - // 优化连线效果 - createConnections(positions, colors) + // 延迟开始初始化 + setTimeout(() => { + isInitializing = true + initStartTime = Date.now() + }, config.initDelay) } const createConnections = (positions, colors) => { @@ -216,16 +231,41 @@ const updateParticles = (time) => { if (!particles || !particles.geometry) return 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++) { const i3 = i * 3 - // 优化粒子运动 positions[i3] += Math.sin(time + i * 0.05) * 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 - // 鼠标交互 if (mouse.x !== null && mouse.y !== null) { const dx = positions[i3] - mouse.x * 20 const dy = positions[i3 + 1] - mouse.y * 20