HTML单页面实现量子纠缠效果

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>
相关推荐
2401_882727572 分钟前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
会发光的猪。37 分钟前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
天下代码客1 小时前
【vue】vue中.sync修饰符如何使用--详细代码对比
前端·javascript·vue.js
猫爪笔记1 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
前端李易安1 小时前
Webpack 热更新(HMR)详解:原理与实现
前端·webpack·node.js
红绿鲤鱼1 小时前
React-自定义Hook与逻辑共享
前端·react.js·前端框架
Domain-zhuo2 小时前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式
小丁爱养花2 小时前
前端三剑客(三):JavaScript
开发语言·前端·javascript
ZwaterZ2 小时前
vue el-table表格点击某行触发事件&&操作栏点击和row-click冲突问题
前端·vue.js·elementui·c#·vue
码农六六2 小时前
vue3封装Element Plus table表格组件
javascript·vue.js·elementui