Three.js实现一个动态的 3D 点阵波浪效果

使用 Three.js 创建了一个动态的 3D 点阵波浪效果,可用作页面背景。它通过粒子系统生成点阵,并根据鼠标移动和时间动态调整点的位置和大小,形成波浪效果。用户可以通过组件的 props 控制波浪的长度、颜色和位置。

javascript 复制代码
<template>
  <div id="iviewBg"></div>
</template>
  
<script>
  import * as THREE from "three";
  const SEPARATION = 100;
  
  export default {
    name: 'PointsWaves',
    props: {
      //控制x轴波浪的长度
      amountX: {
        type: Number,
        default: 50,
      },
      //控制y轴波浪的长度
      amountY: {
        type: Number,
        default: 50,
      },
      //控制点颜色
      color: {
        type: String,
        default: "#8a8a8a",
      },
      //控制波浪的位置
      top: {
        type: Number,
        default: 350,
      },
    },
    data() {
      return {
        container: null,
        camera: null,
        scene: null,
        renderer: null,
        particles: null,
        count: 0,
        mouseX: 0,
        windowHalfX: 0,
      };
    },
    mounted() {
      this.windowHalfX = window.innerWidth / 2;
      this.init();
      this.animate();
    },
    methods: {
      init() {
        this.container = document.createElement("div");
        document.getElementById("iviewBg").appendChild(this.container);
  
        //创建透视相机
        this.camera = new THREE.PerspectiveCamera(
          55, //摄像机视锥体垂直视野角度
          window.innerWidth / window.innerHeight, //摄像机视锥体长宽比
          1, //摄像机视锥体近端面
          10000 //摄像机视锥体远端面
        );
  
        //设置相机z轴视野
        this.camera.position.z = 1000;
  
        //创建场景
        this.scene = new THREE.Scene();
  
        const numParticles = this.amountX * this.amountY;
  
        const positions = new Float32Array(numParticles * 3);
        const scales = new Float32Array(numParticles);
  
        let i = 0,
          j = 0;
  
        // 初始化粒子位置和大小
        for (let ix = 0; ix < this.amountX; ix++) {
          for (let iy = 0; iy < this.amountY; iy++) {
            positions[i] = ix * SEPARATION - (this.amountX * SEPARATION) / 2; // x
            positions[i + 1] = 0; // y
            positions[i + 2] = iy * SEPARATION - (this.amountY * SEPARATION) / 2; // z
            scales[j] = 1;
            i += 3;
            j++;
          }
        }
  
        //是面片、线或点几何体的有效表述。包括顶点位置,面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值。使用 BufferGeometry 可以有效减少向 GPU 传输上述数据所需的开销
        const geometry = new THREE.BufferGeometry();
        geometry.setAttribute(
          "position",
          new THREE.BufferAttribute(positions, 3)
        );
        geometry.setAttribute("scale", new THREE.BufferAttribute(scales, 1));
  
        //着色器材质(ShaderMaterial),设置球的大小,颜色,等
        const material = new THREE.ShaderMaterial({
          uniforms: {
            //设置球的颜色
            color: { value: new THREE.Color(this.color) },
          },
          //控制球的大小
          vertexShader:
            "attribute float scale; void main() {vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );gl_PointSize = scale * ( 300.0 / - mvPosition.z );gl_Position = projectionMatrix * mvPosition;}",
          fragmentShader:
            "uniform vec3 color;void main() {if ( length( gl_PointCoord - vec2( 0.5, 0.5 ) ) > 0.475 ) discard;gl_FragColor = vec4( color, 1.0 );}",
        });
  
        //一个用于显示点的类。
        this.particles = new THREE.Points(geometry, material);
        //往场景中添加点
        this.scene.add(this.particles);
  
        //alpha - canvas是否包含alpha (透明度)。默认为 false。
        //渲染器的背景色默认为黑色,设置渲染器的背景色为透明
        this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setClearAlpha(0);
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.container.appendChild(this.renderer.domElement);
  
        //显示右上角fps框
        // stats = new Stats();
        //   container.appendChild(stats.dom);
  
        this.container.style.touchAction = "none";
        //监听鼠标移动事件
        this.container.addEventListener("pointermove", this.onPointerMove);
  
        //调整波浪的位置
        this.container.style.position = "relative";
        this.container.style.top = `${this.top}px`;
  
        window.addEventListener("resize", this.onWindowResize);
      },
      render() {
        this.camera.position.x += (this.mouseX - this.camera.position.x) * 0.05;
        this.camera.position.y = 400;
        this.camera.lookAt(this.scene.position);
  
        const positions = this.particles.geometry.attributes.position.array;
        const scales = this.particles.geometry.attributes.scale.array;
  
        // 设置粒子位置和大小
        let i = 0,
          j = 0;
        for (let ix = 0; ix < this.amountX; ix++) {
          for (let iy = 0; iy < this.amountY; iy++) {
            positions[i + 1] =
              Math.sin((ix + this.count) * 0.3) * 50 +
              Math.sin((iy + this.count) * 0.5) * 50;
  
            scales[j] =
              (Math.sin((ix + this.count) * 0.3) + 1) * 10 +
              (Math.sin((iy + this.count) * 0.5) + 1) * 10;
  
            i += 3;
            j++;
          }
        }
  
        this.particles.geometry.attributes.position.needsUpdate = true;
        this.particles.geometry.attributes.scale.needsUpdate = true;
  
        this.renderer.render(this.scene, this.camera);
  
        this.count += 0.1;
      },
      onWindowResize() {
        this.windowHalfX = window.innerWidth / 2;
        this.camera.aspect = window.innerWidth / window.innerHeight;
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(window.innerWidth, window.innerHeight);
      },
      onPointerMove(event) {
        //console.log(event);
        if (event.isPrimary === false) return;
        this.mouseX = event.clientX - this.windowHalfX;
      },
      animate() {
        requestAnimationFrame(this.animate);
        this.render();
        //fps 实时更新
        // stats.update();
      },
    },
  };
</script>
  
<style scoped>
  #iviewBg {
    width: 100vw;
    height: 100vh;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    text-align: center;
    overflow: hidden;
  }
</style>
  
相关推荐
Emily156853598704 小时前
Emerson 1C31129G03 Analog Input Module
java·开发语言·前端·plc·emerson·1c31129g03·input module
To_OC9 小时前
面试被问了三回三栏布局,这次我终于把 BFC 那层窗户纸捅破了
前端·css·面试
To_OC9 小时前
啃完 TS 工具类型我发现:Pick 和 Omit 原来就是一层窗户纸
前端·面试·typescript
风月说与山鬼11 小时前
三、大括号语法
前端·react.js
kyriewen11 小时前
我把最常踩的8个CORS跨域报错整理了一遍——第8个去年还不存在
前端·javascript
Csvn12 小时前
🕰️ 闭包 + setTimeout 的 5 个经典陷阱:为什么定时器看到的永远不是最新的值?
前端
lilian23312 小时前
Harmony os 技术实战|拼豆制图27:用单字符编码承载 50 张 70×70 图纸
前端·数据库·华为·harmonyos
CarIise12 小时前
CSS选择器与样式关联
前端·css·tensorflow
里欧跑得慢14 小时前
CSS 模块化架构的演进:BEM、CSS Modules 到 CSS-in-JS 的反思
前端·css·flutter·web·css-in-js