
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><!DOCTYPE html><html><head><style>body { margin: 0; overflow: hidden; background: 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>