feat: 在 ParticleBackground 组件中添加粒子初始化延迟和渐进式显示功能,优化粒子透明度处理,提升动画效果和用户体验。
This commit is contained in:
parent
a1e5bfd74d
commit
112bc980b5
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user