bash
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!DOCTYPE html>
<html>
<head>
<style>
body { margin: 0; overflow: hidden; background: #000; }
canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
let camera, scene, renderer;
let particleSystemInner1, particleSystemInner2;
let particleSystemOuter1, particleSystemOuter2;
let connectionParticles = [];
let time = 0;
const COLOR_1 = 0x00ff00; // 绿色
const COLOR_2 = 0xff0000; // 红色
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 20, 80);
camera.lookAt(0, 0, 0);
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
scene.add(new THREE.AmbientLight(0x222222));
// 增强光源效果
[
[COLOR_1, [-20, 0, 0], 3],
[COLOR_2, [20, 0, 0], 3],
[COLOR_1, [0, 30, 0], 2],
[COLOR_2, [0, -30, 0], 2]
].forEach(([color, [x, y, z], intensity]) => {
const light = new THREE.PointLight(color, intensity, 100);
light.position.set(x, y, z);
scene.add(light);
});
createParticleSystems();
createConnectionSystem();
window.addEventListener('resize', onWindowResize, false);
}
function createParticleSystem(center, color, isInner = false) {
const particles = [];
const particleCount = isInner ? 8000 : 10000; // 增加粒子数量
const baseRadius = isInner ? 8 : 10;
const radiusVariation = isInner ? 0.5 : 1.5; // 减小外层变化范围
for (let i = 0; i < particleCount; i++) {
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(Math.random() * 2 - 1);
const radius = baseRadius + (isInner ? 0 : Math.random() * radiusVariation);
const particle = {
baseRadius: radius,
theta: theta,
phi: phi,
position: new THREE.Vector3(),
basePosition: new THREE.Vector3(
radius * Math.sin(phi) * Math.cos(theta),
radius * Math.sin(phi) * Math.sin(theta),
radius * Math.cos(phi)
)
};
particles.push(particle);
}
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const material = new THREE.PointsMaterial({
color: color,
size: isInner ? 0.15 : 0.12,
transparent: true,
opacity: isInner ? 0.9 : 0.7,
blending: THREE.AdditiveBlending,
depthWrite: false
});
const system = new THREE.Points(geometry, material);
system.userData = {
particles,
isInner,
center: center.clone()
};
scene.add(system);
return system;
}
function createParticleSystems() {
const pos1 = new THREE.Vector3(-20, 0, 0);
const pos2 = new THREE.Vector3(20, 0, 0);
// 内层粒子系统
particleSystemInner1 = createParticleSystem(pos1, COLOR_1, true);
particleSystemInner2 = createParticleSystem(pos2, COLOR_2, true);
// 外层粒子系统
particleSystemOuter1 = createParticleSystem(pos1, COLOR_2, false);
particleSystemOuter2 = createParticleSystem(pos2, COLOR_1, false);
}
function createConnectionSystem() {
function createFlow(startPos, endPos, color) {
const particleCount = 4000;
const particles = [];
for (let i = 0; i < particleCount; i++) {
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(Math.random() * 2 - 1);
const radius = Math.random() * 4;
const startOffset = new THREE.Vector3(
radius * Math.sin(phi) * Math.cos(theta),
radius * Math.sin(phi) * Math.sin(theta),
radius * Math.cos(phi)
);
particles.push({
position: new THREE.Vector3(),
startOffset: startOffset,
baseOffset: new THREE.Vector3(
(Math.random() - 0.5) * 4, // 减小随机偏移
(Math.random() - 0.5) * 4,
(Math.random() - 0.5) * 4
),
speed: 0.003 + Math.random() * 0.005,
progress: Math.random()
});
}
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const material = new THREE.PointsMaterial({
color: color,
size: 0.2,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending,
depthWrite: false
});
const points = new THREE.Points(geometry, material);
points.userData = {
particles,
startPos: startPos.clone(),
endPos: endPos.clone()
};
scene.add(points);
connectionParticles.push(points);
}
const pos1 = new THREE.Vector3(-20, 0, 0);
const pos2 = new THREE.Vector3(20, 0, 0);
createFlow(pos1, pos2, COLOR_1);
createFlow(pos2, pos1, COLOR_2);
}
function updateParticleSystem(system, time) {
const positions = system.geometry.attributes.position.array;
const particles = system.userData.particles;
const isInner = system.userData.isInner;
const center = system.userData.center;
particles.forEach((particle, i) => {
const rotationMatrix = new THREE.Matrix4();
rotationMatrix.makeRotationY(time * 1.5);
// 计算当前半径(外层有较小的呼吸效果)
let currentRadius = particle.baseRadius;
if (!isInner) {
const breathEffect = Math.sin(time * 2) * 0.15 + 1; // 减小呼吸幅度
currentRadius *= breathEffect;
}
// 更新位置
const pos = new THREE.Vector3(
currentRadius * Math.sin(particle.phi) * Math.cos(particle.theta),
currentRadius * Math.sin(particle.phi) * Math.sin(particle.theta),
currentRadius * Math.cos(particle.phi)
);
pos.applyMatrix4(rotationMatrix);
pos.add(center);
positions[i * 3] = pos.x;
positions[i * 3 + 1] = pos.y;
positions[i * 3 + 2] = pos.z;
});
system.geometry.attributes.position.needsUpdate = true;
}
function updateConnectionParticles() {
connectionParticles.forEach(points => {
const positions = points.geometry.attributes.position.array;
const particles = points.userData.particles;
const startPos = points.userData.startPos;
const endPos = points.userData.endPos;
particles.forEach((particle, i) => {
particle.progress += particle.speed;
if (particle.progress > 1) {
particle.progress = 0;
}
const t = particle.progress;
const pos = new THREE.Vector3();
const actualStartPos = startPos.clone().add(particle.startOffset);
const cp1 = new THREE.Vector3().lerpVectors(actualStartPos, endPos, 0.25)
.add(new THREE.Vector3(0, 10 * (Math.random() - 0.5), 0));
const cp2 = new THREE.Vector3().lerpVectors(actualStartPos, endPos, 0.75)
.add(new THREE.Vector3(0, 10 * (Math.random() - 0.5), 0));
pos.x = Math.pow(1-t, 3) * actualStartPos.x +
3 * Math.pow(1-t, 2) * t * cp1.x +
3 * (1-t) * Math.pow(t, 2) * cp2.x +
Math.pow(t, 3) * endPos.x;
pos.y = Math.pow(1-t, 3) * actualStartPos.y +
3 * Math.pow(1-t, 2) * t * cp1.y +
3 * (1-t) * Math.pow(t, 2) * cp2.y +
Math.pow(t, 3) * endPos.y;
pos.z = Math.pow(1-t, 3) * actualStartPos.z +
3 * Math.pow(1-t, 2) * t * cp1.z +
3 * (1-t) * Math.pow(t, 2) * cp2.z +
Math.pow(t, 3) * endPos.z;
const wave = Math.sin(time * 2 + t * Math.PI * 2) * 1.5; // 减小波动幅度
pos.add(particle.baseOffset.clone().multiplyScalar(wave * 0.2));
positions[i * 3] = pos.x;
positions[i * 3 + 1] = pos.y;
positions[i * 3 + 2] = pos.z;
});
points.geometry.attributes.position.needsUpdate = true;
});
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
time += 0.01;
// 更新内外层粒子系统
updateParticleSystem(particleSystemInner1, time);
updateParticleSystem(particleSystemInner2, time);
updateParticleSystem(particleSystemOuter1, time);
updateParticleSystem(particleSystemOuter2, time);
updateConnectionParticles();
camera.position.x = Math.cos(time * 0.1) * 80;
camera.position.z = Math.sin(time * 0.1) * 80;
camera.lookAt(0, 0, 0);
renderer.render(scene, camera);
}
</script>
</body>
</html>