从零开始搭建开源智慧城市项目(六)飞线,飞点。

1,862 阅读2分钟

前言

上一节实现了天空盒、扩散墙、扩散圆,这一节来添加飞线,飞点。

思路

主要思路是通过两个点和高度创建贝塞尔曲线,然后把该曲线的点构造成MeshLine(这个插件可以生成有宽度的线),然后把想要效果的图贴到线上。

  • 飞点: image.png

image.png

  • 飞线

image.png

image.png

这里用到了别人写的MeshLine插件(github.com/spite/THREE… ),对这个代码做了一点点修改,可进行模型材质的uv移动(THREE高版本有问题,我这边用的是127版本没有问题)。 image.png 这个修改是借鉴的deckgldeck.gl/ )具体的修改就是把UV偏移量传进去实现偏移。

代码

import * as THREE from 'three';
import { Geometry } from 'three/examples/jsm/deprecated/Geometry';
import { MeshLine, MeshLineMaterial } from './THREE.MeshLine';

THREE.Geometry = Geometry;
/**
 * 波纹散射图层
 * @param  options.img 照片地址
 * @param  options.lineWidth 线宽度
 * @param  options.side 贴图样式
 * @param  options.camera 相机
 * @param  options.height 高度
 * @param  options.v0 点一
 * @param  options.v1 点二
 * @param  options.speed 流动速度
 * @param  options.el 节目场景元素
 * @param  options.type 线类型分run和top
 * @param  options.maxheight 当类型为top时最高上升高度
 * @param  options.line 存储线
 * @param  options.thing 存储setInterval事件
 * @example
 */
class RunLine {
  constructor(option) {
    this.img = option.img || '';
    this.lineWidth = option.lineWidth || 1;
    this.side = option.side || THREE.FrontSide;
    this.camera = option.camera || '';
    this.height = option.height || 100;
    this.v0 = option.v0 || new THREE.Vector3(0, 0, 0);
    this.v1 = option.v1 || new THREE.Vector3(0, 0, 0);
    this.speed = option.speed / 100 || 0.01;
    this.el = option.el || '';
    this.scene = option.scene || '';
    this.type = option.type || 'run';
    this.maxheight = option.maxheight || 300;
    this.line = '';
    this.thing = '';
    this.creatline();
  }

  creatline() {
    const textureLoader = new THREE.TextureLoader();
    textureLoader.load(this.img, (texture) => {
      this.texture = texture;
      this.texture.anisotropy = 16;
      this.texture.wrapS = THREE.RepeatWrapping; // 每个都重复
      this.texture.wrapT = THREE.RepeatWrapping;
      const resolution = new THREE.Vector2(this.el.offsetWidth, this.el.offsetHeight);
      const material1 = new MeshLineMaterial({
        color: '',
        map: this.texture,
        useMap: true,
        lineWidth: this.lineWidth,
        resolution,
        dashArray: 0, // 破折号之间的长度和间距。(0 -无破折号)
        dashRatio: 0.7, // 定义可见和不可见之间的比率(0 -更可见,1 -更不可见)。
        dashOffset: 1,
        transparent: true,
        sizeAttenuation: 1, // 使线宽不变,不管距离(1个单位是屏幕上的1px)(0 -衰减,1 -不衰减)
        side: THREE.FrontSide,
        depthTest: false,
        blending: THREE.AdditiveBlending,
        near: this.camera.near,
        far: this.camera.far,
      });
      this.line = this.createAnimateLine({
        kind: 'sphere', // 默认不填 为普通 ; 如为sphere,则表示球面建点
        type: 'line', // 默认不填 为MeshLine ; 如为pipe,则表示管道线
        sphereHeightPointsArgs: [this.v0, this.v1],
        material: material1,
        number: 50,
        radius: 3, // 默认
      });
      if (this.type === 'run') {
        this.line1 = this.createAnimateLine({
          kind: 'sphere', // 默认不填 为普通 ; 如为sphere,则表示球面建点
          type: 'line', // 默认不填 为MeshLine ; 如为pipe,则表示管道线
          sphereHeightPointsArgs: [this.v0, this.v1],
          material: material1,
          number: 50,
          radius: 3, // 默认
        });
      }

      this.scene.add(this.line);
      this.scene.add(this.line1);
      const { speed } = this;
      if (this.type === 'run') {
        this.thing = setInterval(() => {
          this.line.material.uniforms.offset.value.x -= speed;
          this.line1.material.uniforms.offset.value.x -= speed;
        }, 30);
      } else if (this.type === 'top') {
        this.thing = setInterval(() => {
          if (this.line.position.y < this.maxheight) {
            this.line.position.y += this.speed * 100;
          } else {
            this.line.position.y = 0;
          }
        }, 30);
      }
    });
  }

  createAnimateLine(option) {
    let curve;

    if (option.kind === 'sphere') {
      // 由两点之间连线成贝塞尔曲线
      const { sphereHeightPointsArgs } = option;
      const vX = (sphereHeightPointsArgs[1].x + sphereHeightPointsArgs[0].x) / 2;
      const vZ = (sphereHeightPointsArgs[1].z + sphereHeightPointsArgs[0].z) / 2;
      if (this.type === 'run') {
        curve = new THREE.CubicBezierCurve3(
          sphereHeightPointsArgs[0],
          new THREE.Vector3(vX, this.height, vZ),
          new THREE.Vector3(vX, this.height, vZ),
          sphereHeightPointsArgs[1],
        );
      } else {
        curve = new THREE.CubicBezierCurve3(
          sphereHeightPointsArgs[0],
          new THREE.Vector3(vX, this.height, vZ),
          new THREE.Vector3(vX, this.height, vZ),
          sphereHeightPointsArgs[1],
        );
      }
    }

    if (option.type === 'line') {
      const geo = new THREE.Geometry();
      geo.setFromPoints(curve.getPoints(100));

      // const geo = new Geometry();
      // geo.vertices = option.sphereHeightPointsArgs;
      const meshLine = new MeshLine();
      meshLine.setGeometry(geo);
      return new THREE.Mesh(meshLine.geometry, option.material);
    } // 使用 meshLine

    return '';
  }

  delete() {
    if (this.line) {
      this.scene.remove(this.line);
      this.scene.remove(this.line1);
    }
    if (this.thing) {
      clearInterval(this.thing);
    }
  }
}

export default RunLine;

调用代码

 this.runline5 = new RunLine({
        img: "n.png",
        camera: camera,
        height: 140,
        v0: new THREE.Vector3(614, 18, 130),
        v1: new THREE.Vector3(-17.5, 111.5, -23),
        el: document.getElementById("scene"),
        scene: scene,
        speed: 1,
        lineWidth: 12,
        type: "run",
      });

效果图

SDGIF_Rusult_1.gif

项目地址github.com/lixiaochjaj…