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>
  
相关推荐
崔庆才丨静觅11 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606112 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了12 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅12 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅13 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅13 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment13 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅13 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊13 小时前
jwt介绍
前端
爱敲代码的小鱼14 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax