ThreeJS

ThreeJS

安装three.js

npm install three

安装轨道控制器

npm install three-orbit-controls

绑定轨道控制器

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

const controls = new OrbitControls(camera, renderer.domElement)//创建控件对象
controls.addEventListener('change',()=>{
    renderer.render(scene, camera)//监听鼠标,键盘事件
})

如果循环渲染则需要在渲染函数中更新控制器

startFrame(){
        this.controls.update();
        // 使用浏览器自带的请求动画帧函数不断的进行渲染
        this.frameNum = requestAnimationFrame(this.startFrame);
}

创建场景

const scene = new THREE.Scene();

为场景添加光源

//场景光源,不会有阴影
const ambient = new THREE.AmbientLight(0xffffff, 0.5);
const light = new THREE.PointLight(0xffffff, 0.2);//点光源,color:灯光颜色,intensity:光照强度

//灯光坐标
light.position.set(200,200,800);

//添加进场景
this.scene.add( ambient );
this.scene.add( light );

几何体相关十二、Three.js中的常用几何体 - 知乎 (zhihu.com)

创建材质

  • 可传入参数:color材质颜色
const mat = new THREE.MeshStandardMaterial({
  color: 0xBDF7F1, // 材质的颜色
});

创建几何体

  • 参数:
    • 宽度
    • 高度
    • 深度
const geometry = new THREE.BoxGeometry(100, 100, 200, 1, 1, 1);

自定义几何体

通过二维图形绘制三维立体

  • 创建梯形
    • 绘制二维图形
    • 通过ExtrudeGeometry拓展为三维图形
createLadderType(widthU,widthD, height, depth, angleX,angleY, material, x, y, z, name){
    let shape = new THREE.Shape();
    let oX = 0,oY = 0;
    //起始绘制点
    shape.moveTo(oX,oY);
    //绘制直线...
    shape.lineTo(oX+widthD/2,oY+0);
    shape.lineTo(oX-(widthD-widthU)/2 + widthD/2,oY+height);
    shape.lineTo(oX+(widthD-widthU)/2 - widthD/2,oY+height);
    shape.lineTo(oX-widthD/2,oY+0);


    //得到拓展设置对象,depth为拓展深度
    function extend2D(depth){
        let extrudeSetting = {
            steps:1,
            //深度
            depth:depth,
            //是否斜角
            bevelEnabled:true,
            //原始形状上斜角厚度
            bevelThickness:0.2,
            //斜角大小
            bevelSize:-0.1,
            //斜角与原始形状延伸距离
            bevelOffset:0,
            //斜角分段层数
            bevelSegments:3,
        }
        return extrudeSetting;
    }

    //通过ExtrudeGeometry获得立体图形
    let geometry = new THREE.ExtrudeGeometry(shape,extend2D(2));
    let mesh = new THREE.Mesh( geometry, material ) ;
    mesh.position.x = x;
    mesh.position.y = z;
    mesh.position.z = y;
    mesh.rotation.y += angleY * Math.PI;  //-逆时针旋转,+顺时针
    mesh.rotation.x += angleX * Math.PI;  //-逆时针旋转,+顺时针
    mesh.name = name;
    this.scene.add( mesh );
}

为场景添加网格对象

创建网格

  • 参数需求
    • 几何体对象
    • 材质对象
const mesh = new THREE.Mesh(geometry, mat);

定义中心坐标

mesh.position.x= 0;
mesh.position.y = 0;
mesh.position.z = 0;

//或者这样定义
mesh.position.set(x,y,z);

添加网格对象

this.scene.add(mesh);

创建摄像机

//windowWidth,windowHeight 传入窗口宽度和高度
let camera = new THREE.PerspectiveCamera(45, this.windowWidth / this.windowHeight, 0.1, 100000);
camera.position.set(0, 800, 1500);
camera.lookAt(new THREE.Vector3(0, 0, 0));

this.camera = camera;

创建渲染器

//创建一个WebGL渲染器
const renderer = new THREE.WebGLRenderer()
const width = window.innerWidth, height = window.innerHeight;
renderer.setClearColor(0x4682B4,1.0);
renderer.setSize(width,height)//设置渲染区尺寸
renderer.render(scene,camera)//执行渲染操作、指定场景、相机作为参数
return renderer;

绑定渲染组件

document.getElementById('my-three')?.appendChild(this.tRender.domElement);

清除渲染组件

此方法仅清除组件,但还需要手动关闭渲染资源,否则会造成资源占用

/*清除dom内3D展示*/
destroyThree(){
  let parent = document.getElementById('my-three');
  let childNodes = parent.childNodes;
  for (let i = childNodes.length - 1; i >= 0; i--) { // 一定要倒序,正序是删不干净的,可自行尝试
    parent.removeChild(childNodes[i]);
  }
}

关闭渲染资源

/*关闭组件*/
close(){
  cancelAnimationFrame(this.frameNum);
  this.tRender.forceContextLoss();
  this.tRender.content = null;
  this.tRender = null;
  this.camera = null;
  this.scene = null;
}

(33条消息) 43 Three.js自定义二维图形THREE.ShapeGeometry_暮志未晚Webgl的博客-CSDN博客_shapegeometry

循环渲染

startFrame(){
  if (this.isRender === true){
    //更新控制器
    this.controls.update();
    /*构建前清除缓存*/
    this.clearC();

    //-------------------


    //重新构建场景...
      
    //重新渲染场景,摄像机对象不变
    this.tRender.render(this.scene, this.camera);

    //设置下一帧更新参数...

    //------------------

    // 使用浏览器自带的请求动画帧函数不断的进行渲染
    this.frameNum = requestAnimationFrame(this.startFrame);
  }
}

循环渲染注意点

每次渲染前清除缓存占用

this.tRender.dispose();
this.scene.clear();
THREE.Cache.clear();

停止循环渲染工作

this.isRender = false;
cancelAnimationFrame(this.frameNum);
this.tRender.forceContextLoss();
this.tRender.content = null;
this.tRender = null;
this.camera = null;
this.scene = null;

这里使用isRender控制渲染条件

vue组件路由切换需要手动销毁

beforeRouteLeave: 路由离开函数

等待组件销毁完毕跳转路由

beforeRouteLeave(to,form,next){
  setTimeout(()=>{
    this.destroyThreeD();
    setTimeout(()=>{
      next();
    },100)
  },2)
},

键盘监听键位ID大全

keycode 8 = BackSpace BackSpace
keycode 9 = Tab Tab
keycode 12 = Clear
keycode 13 = Enter
keycode 16 = Shift_L
keycode 17 = Control_L
keycode 18 = Alt_L
keycode 19 = Pause
keycode 20 = Caps_Lock
keycode 27 = Escape Escape
keycode 32 = space space
keycode 33 = Prior
keycode 34 = Next
keycode 35 = End
keycode 36 = Home
keycode 37 = Left
keycode 38 = Up
keycode 39 = Right
keycode 40 = Down
keycode 41 = Select
keycode 42 = Print
keycode 43 = Execute
keycode 45 = Insert
keycode 46 = Delete
keycode 47 = Help
keycode 48 = 0 equal braceright
keycode 49 = 1 exclam onesuperior
keycode 50 = 2 quotedbl twosuperior
keycode 51 = 3 section threesuperior
keycode 52 = 4 dollar
keycode 53 = 5 percent
keycode 54 = 6 ampersand
keycode 55 = 7 slash braceleft
keycode 56 = 8 parenleft bracketleft
keycode 57 = 9 parenright bracketright
keycode 65 = a A
keycode 66 = b B
keycode 67 = c C
keycode 68 = d D
keycode 69 = e E EuroSign
keycode 70 = f F
keycode 71 = g G
keycode 72 = h H
keycode 73 = i I
keycode 74 = j J
keycode 75 = k K
keycode 76 = l L
keycode 77 = m M mu
keycode 78 = n N
keycode 79 = o O
keycode 80 = p P
keycode 81 = q Q at
keycode 82 = r R
keycode 83 = s S
keycode 84 = t T
keycode 85 = u U
keycode 86 = v V
keycode 87 = w W
keycode 88 = x X
keycode 89 = y Y
keycode 90 = z Z
keycode 96 = KP_0 KP_0
keycode 97 = KP_1 KP_1
keycode 98 = KP_2 KP_2
keycode 99 = KP_3 KP_3
keycode 100 = KP_4 KP_4
keycode 101 = KP_5 KP_5
keycode 102 = KP_6 KP_6
keycode 103 = KP_7 KP_7
keycode 104 = KP_8 KP_8
keycode 105 = KP_9 KP_9
keycode 106 = KP_Multiply KP_Multiply
keycode 107 = KP_Add KP_Add
keycode 108 = KP_Separator KP_Separator
keycode 109 = KP_Subtract KP_Subtract
keycode 110 = KP_Decimal KP_Decimal
keycode 111 = KP_Divide KP_Divide
keycode 112 = F1
keycode 113 = F2
keycode 114 = F3
keycode 115 = F4
keycode 116 = F5
keycode 117 = F6
keycode 118 = F7
keycode 119 = F8
keycode 120 = F9
keycode 121 = F10
keycode 122 = F11
keycode 123 = F12
keycode 124 = F13
keycode 125 = F14
keycode 126 = F15
keycode 127 = F16
keycode 128 = F17
keycode 129 = F18
keycode 130 = F19
keycode 131 = F20
keycode 132 = F21
keycode 133 = F22
keycode 134 = F23
keycode 135 = F24
keycode 136 = Num_Lock
keycode 137 = Scroll_Lock
keycode 187 = acute grave
keycode 188 = comma semicolon
keycode 189 = minus underscore
keycode 190 = period colon
keycode 192 = numbersign apostrophe
keycode 210 = plusminus hyphen macron
keycode 212 = copyright registered
keycode 213 = guillemotleft guillemotright
keycode 214 = masculine ordfeminine
keycode 215 = ae AE
keycode 216 = cent yen
keycode 217 = questiondown exclamdown
keycode 218 = onequarter onehalf threequarters
keycode 220 = less greater bar
keycode 221 = plus asterisk asciitilde
keycode 227 = multiply division
keycode 228 = acircumflex Acircumflex
keycode 229 = ecircumflex Ecircumflex
keycode 230 = icircumflex Icircumflex
keycode 231 = ocircumflex Ocircumflex
keycode 232 = ucircumflex Ucircumflex
keycode 233 = ntilde Ntilde
keycode 234 = yacute Yacute
keycode 235 = oslash Ooblique
keycode 236 = aring Aring
keycode 237 = ccedilla Ccedilla
keycode 238 = thorn THORN
keycode 239 = eth ETH
keycode 240 = diaeresis cedilla currency
keycode 241 = agrave Agrave atilde Atilde
keycode 242 = egrave Egrave
keycode 243 = igrave Igrave
keycode 244 = ograve Ograve otilde Otilde
keycode 245 = ugrave Ugrave
keycode 246 = adiaeresis Adiaeresis
keycode 247 = ediaeresis Ediaeresis
keycode 248 = idiaeresis Idiaeresis
keycode 249 = odiaeresis Odiaeresis
keycode 250 = udiaeresis Udiaeresis
keycode 251 = ssharp question backslash
keycode 252 = asciicircum degree
keycode 253 = 3 sterling
keycode 254 = Mode_switch

使用event对象的keyCode属性判断输入的键值
 eg:if(event.keyCode==13)alert(“enter!”);
键值对应表
A  0X65  U   0X85
B  0X66  V   0X86
C  0X67  W   0X87
D  0X68  X   0X88
E  0X69  Y   0X89
F  0X70  Z   0X90
G  0X71  0   0X48
H  0X72  1   0X49
I   0X73  2   0X50
J   0X74  3   0X51
K  0X75  4   0X52
L  0X76  5   0X53
M  0X77  6   0X54
N  0X78  7   0X55
O  0X79  8   0X56
P  0X80  9   0X57
Q  0X81 ESC  0X1B
R  0X82 CTRL  0X11
S  0X83 SHIFT 0X10
T  0X84 ENTER 0XD

渡口实践3D渲染组件

<template>
<div>
  <div id="my-three"></div>
</div>
</template>

<script>
import * as THREE from "three"
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'



export default {
  mounted() {
    this.clearData();

    this.sessionStorageInfoLoad();
    this.ferryLanding();
    setTimeout(()=>{
      this.main();
    },200);
  },
  name: "ferryThreeDisplay",
  data: ()=>{
    return {
      windowWidth: 1480,
      windowHeight: 580,
      scene:undefined,
      nFid:0,
      /*本地渡船数据*/
      ferryRes:undefined,
      /*车辆装载信息*/
      carLoadInfo:undefined,
      /*建模渡船数据*/
      ferry:[],
      /*行驶渡船建模数据*/
      functionFerry:[],
      camera:undefined,
      /*轨道控制器*/
      controls:undefined,
      /*渲染器*/
      tRender:undefined,
      /*是否循环渲染*/
      isRender:true,
      frameNum:0
    }
  },

  methods:{
    clearData(){

    },
    ferryLanding(){
      for (let f of this.ferry) {
        if (f){
          /*尾部对齐*/
          let rowPlusFactor = 20;
          let landLine = 400;
          f.y = (-f.row * rowPlusFactor /2)  //船甲板计算
              - 50 //上船板计算
              + landLine;
        }
      }
    },

    sessionStorageInfoLoad(){
      /*更新当前页面泊岸渡船渲染数据源*/
      this.updateFerryInfo();
      this.updateCarLoadInfo();

      /*更新当前页面行驶渲染数据源*/
      this.updateFunctionFerry();
      this.updateFunctionFerryCarLoadInfo();
    },

    updateFunctionFerry() {
      /*获取本地存储数据*/
      let functionFerryMap = sessionStorage.getItem("functionFerryMap");
      if (functionFerryMap){
        this.functionFerryMap = JSON.parse(functionFerryMap);
        for (let key in this.functionFerryMap) {
          let ferry = this.functionFerryMap[key];
          if (ferry){
            /*包装渡船信息加入渲染数组*/
            let fid = ferry.fid;
            let row = ferry.row;
            let col = ferry.col;

            /*判断是否已经存在重复信息*/
            let continueFlag = false;
            for (let functionFerryInfo of this.functionFerry) {
              if (functionFerryInfo.fid === fid){
                continueFlag = true;
              }
            }
            if (continueFlag === true){
              continue;
            }

            this.functionFerry.push(
                {
                  fid: fid,
                  row: row,
                  col: col,
                  x: fid * 300,
                  y: 0,
                  z: 0,
                  trackStatus:new Array(ferry.ferryTrackNumber),
                  loadLocationList: [...ferry.loadLocationList]
                });
          }
        }
      }
    },

    updateFunctionFerryCarLoadInfo(){
      //为渡船模型添加装载信息
      for (let ferry of this.functionFerry) {
        let loadLocationList = ferry.loadLocationList;

        if (loadLocationList.length > 0){
          for (let carLoadInfo of loadLocationList) {
            if (carLoadInfo){
              let carRow = carLoadInfo.carRow;
              let carCol = carLoadInfo.carCol;
              let carTypeColor = carLoadInfo.carTypeColor;
              let fid = carLoadInfo.fid;
              let toFerryTrackId = carLoadInfo.toFerryTrackId;
              if (ferry && ferry.fid === fid){
                if(ferry.trackStatus[toFerryTrackId]){
                  ferry.trackStatus[toFerryTrackId].unshift({carRow,carCol,carTypeColor});
                }else{
                  ferry.trackStatus[toFerryTrackId] = [];
                  ferry.trackStatus[toFerryTrackId].unshift({carRow,carCol,carTypeColor});
                }
              }
            }
          }
        }

      }
    },

    updateFerryInfo(){
      /*获取本地存储数据*/
      let randomInitFerryRes = sessionStorage.getItem("randomInitFerryRes");
      if (randomInitFerryRes){
        this.ferryRes = JSON.parse(randomInitFerryRes);
        for (let ferryRe of this.ferryRes) {
          if (ferryRe){
            /*包装渡船信息加入渲染数组*/
            let fid = ferryRe.fid;
            let row = ferryRe.row;
            let col = ferryRe.col;
            this.ferry.push(
                {
                  fid: fid,
                  row: row,
                  col: col,
                  x: fid * 300,
                  y: 0,
                  z: 0,
                  trackStatus:new Array(ferryRe.ferryTrackNumber),
                  loadLocationList: [...ferryRe.loadLocationList]
                });
          }
        }
      }
    },

    updateCarLoadInfo(){
        //为渡船模型添加装载信息
        for (let ferry of this.ferry) {
          let loadLocationList = ferry.loadLocationList;

          if (loadLocationList.length > 0){
            for (let carLoadInfo of loadLocationList) {
              if (carLoadInfo){
                let carRow = carLoadInfo.carRow;
                let carCol = carLoadInfo.carCol;
                let carTypeColor = carLoadInfo.carTypeColor;
                let fid = carLoadInfo.fid;
                let toFerryTrackId = carLoadInfo.toFerryTrackId;
                if (ferry && ferry.fid === fid){
                  if(ferry.trackStatus[toFerryTrackId]){
                    ferry.trackStatus[toFerryTrackId].unshift({carRow,carCol,carTypeColor});
                  }else{
                    ferry.trackStatus[toFerryTrackId] = [];
                    ferry.trackStatus[toFerryTrackId].unshift({carRow,carCol,carTypeColor});
                  }
                }
              }
            }
          }

      }
    },

    createWater(){
      const floorMat = new THREE.MeshStandardMaterial({
        color: 0xBDF7F1, // 材质的颜色
      });
      let wL = 20000;
      let wW = 100000;
      const floorGeometry = new THREE.BoxGeometry(wW, wL, 200, 1, 1, 1);
      const floorMesh = new THREE.Mesh(floorGeometry, floorMat);
      floorMesh.position.y = -100;
      floorMesh.position.z = 400-wL/2;
      floorMesh.receiveShadow = true;
      floorMesh.rotation.x = -Math.PI / 2.0;
      this.scene.add(floorMesh);
    },

    createGround(){
      const floorMat = new THREE.MeshStandardMaterial({
        color: 0xADADAD, // 材质的颜色
      });
      /*陆地长宽*/
      let landL = 5000;
      let landW = 100000;
      const floorGeometry = new THREE.BoxGeometry(landW, landL, 200, 1, 1, 1);
      const floorMesh = new THREE.Mesh(floorGeometry, floorMat);
      /*L*/
      floorMesh.position.x = 0;
      /*高*/
      floorMesh.position.y = -70;
      floorMesh.position.z = 400 + landL/2;
      floorMesh.receiveShadow = true;
      floorMesh.rotation.x = -Math.PI / 2.0;
      this.scene.add(floorMesh);
    },
    createScene(){
      /*创建场景*/
      const scene = new THREE.Scene();
      this.scene = scene;
    },
    createCube(width,height,depth,x,y,z){
      const cube = new THREE.BoxGeometry(width, height, depth);//长宽高立方体
      //创建材质(外观)
      const material = new THREE.MeshLambertMaterial({
        color: 0x969595,
        transparent: false,//开启透明度
        opacity: 1 //设置透明度
      });

      const mesh = new THREE.Mesh(cube,material);//网络模型对象Mesh
      mesh.position.set(x, y, z);
      mesh.castShadow = true; // 显示阴影

      this.scene.add(mesh)
    },

    createLightAndPushInScene(){
      //添加光源 //会照亮场景里的全部物体(氛围灯),前提是物体是可以接受灯光的,这种灯是无方向的,即不会有阴影。
      const ambient = new THREE.AmbientLight(0xffffff, 0.5);
      const light = new THREE.PointLight(0xffffff, 0.2);//点光源,color:灯光颜色,intensity:光照强度

      light.position.set(200,200,800);

      this.scene.add( ambient );
      this.scene.add( light );
    },

    createCamera(){
      let camera = new THREE.PerspectiveCamera(45, this.windowWidth / this.windowHeight, 0.1, 100000);
      camera.position.set(0, 800, 1500);
      camera.lookAt(new THREE.Vector3(0, 0, 0));

      this.camera = camera;
    },
    createFerry(ferry){

      let {row,col,x,y,z} = ferry;

      let bW = 3;
      //底层厚度
      let under = 50;

      /*侧板高度*/
      let bH = 50;

      row *= 20;
      col *= 20;

      this.createCubeWall(col, under, row, 0, new THREE.MeshPhongMaterial({color: 0xBAA6A6}), x, z, y, "甲板")


      this.createCubeWall(bW, bH,row , 0, new THREE.MeshPhongMaterial({color: 0xafc0ca}), x-col/2, z+bH/2, y, "侧板左")

      //渡船口
      this.createCubeWall(bW, bH,row , 0, new THREE.MeshPhongMaterial({color: 0xafc0ca}), x+col/2, z+bH/2, y, "侧板左")
      this.createCubeWall(col, bH,bW, 0, new THREE.MeshPhongMaterial({color: 0xafc0ca}), x, z+bH/2, y-row/2, "前板")

      this.createCubeWall(col/6, bH,bW, 0, new THREE.MeshPhongMaterial({color: 0xafc0ca}), x-col*3/7, z+bH/2, y+row/2, "后板")
      this.createCubeWall(col/6, bH,bW, 0, new THREE.MeshPhongMaterial({color: 0xafc0ca}), x+col*3/7, z+bH/2, y+row/2, "后板")

      this.createFerryHead(row,col,bH,x,y,z);
      this.createLadderType(col/3,col-col/3,bH,
          bW,
          1/2,0,
          new THREE.MeshBasicMaterial( { color: 0x707070 }),
          x,y+row/2,z+bH/2,
          "上船板");

      this.createLoadCar(ferry);

    },

    createLoadCar(ferry){
      let {row,col,x,y,z,trackStatus} = ferry;

      let trackNum = trackStatus.length;

      let trackCol = col * 20 / trackNum - 6;

      function toMidTrackDistance(trackId){
        if (trackNum % 2 == 0){
          return (trackNum/2 - trackId - 1/2) * (trackCol +5);
        }else {
          return ((trackNum-1)/2 - trackId) * (trackCol+5);
        }
      }

      function toFerryMidDistance(reRow,useFulRow){
        let rowDis = useFulRow - (reRow/2);
        return rowDis;
      }

      /*获取到轨道列渲染中心点*/
      let colDistance = [];
      for (let i = 0; i < trackNum; i++) {
        colDistance.push(toMidTrackDistance(i));
      }

      let trackId = 0;
      for (let track of trackStatus) {
        let colDis = colDistance[trackId];
        if (track){
          /*初始化第一辆车间隔*/
          let useFulRow = row*20/2 - 20;
          for (const car of track) {
            let reCol = car.carCol * 15;
            let reRow = car.carRow * 20;
            let carTypeColor = car.carTypeColor;
            let rowDis = toFerryMidDistance(reRow,useFulRow);

            this.createCubeWall(reCol,40,reRow,0,
                new THREE.MeshPhongMaterial({color: parseInt(carTypeColor,16)}),
                x-colDis,z+40,y-rowDis,
                "车");
            /*渲染可用row值减少*/
            useFulRow -= (reRow+4);
          }

        }
        trackId++;
      }

    },

    createLadderType(widthU,widthD, height, depth, angleX,angleY, material, x, y, z, name){
      let shape = new THREE.Shape();
      let oX = 0,oY = 0;
      shape.moveTo(oX,oY);
      shape.lineTo(oX+widthD/2,oY+0);
      shape.lineTo(oX-(widthD-widthU)/2 + widthD/2,oY+height);
      shape.lineTo(oX+(widthD-widthU)/2 - widthD/2,oY+height);
      shape.lineTo(oX-widthD/2,oY+0);


      function extend2D(depth){
        let extrudeSetting = {
          steps:1,
          //深度
          depth:depth,
          //是否斜角
          bevelEnabled:true,
          //原始形状上斜角厚度
          bevelThickness:0.2,
          //斜角大小
          bevelSize:-0.1,
          //斜角与原始形状延伸距离
          bevelOffset:0,
          //斜角分段层数
          bevelSegments:3,
        }
        return extrudeSetting;
      }

      let geometry = new THREE.ExtrudeGeometry(shape,extend2D(2));
      let mesh = new THREE.Mesh( geometry, material ) ;
      mesh.position.x = x;
      mesh.position.y = z;
      mesh.position.z = y;
      mesh.rotation.y += angleY * Math.PI;  //-逆时针旋转,+顺时针
      mesh.rotation.x += angleX * Math.PI;  //-逆时针旋转,+顺时针
      mesh.name = name;
      this.scene.add( mesh );
    },

    createFerryHead(row,col,bH,x,y,z){
      let circleGeometry = new THREE.CylinderGeometry (col/2,col/2,50,100,1,false,0,Math.PI)

      circleGeometry.rotateY(Math.PI/2);

      //材质对象
      let material = new THREE.LineBasicMaterial({
        color: 0xA39492
      });

      //线条模型对象
      let cycle = new THREE.Mesh(circleGeometry, material);

      cycle.position.x = x;
      cycle.position.y = z + bH/2;
      cycle.position.z = y + -row/2;

      this.scene.add(cycle); //线条对象添加到场景中
    },

    createCubeWall(width, height, depth, angle, material, x, y, z, name) {
      let cubeGeometry = new THREE.BoxGeometry(width, height, depth);
      let cube = new THREE.Mesh(cubeGeometry, material);
      cube.position.x = x;
      cube.position.y = y;
      cube.position.z = z;
      cube.rotation.y += angle * Math.PI;  //-逆时针旋转,+顺时针
      cube.name = name;
      this.scene.add(cube);
    },

    renderer(scene,camera){
      //创建一个WebGL渲染器
      const renderer = new THREE.WebGLRenderer()
      const width = this.windowWidth, height = this.windowHeight;
      renderer.setClearColor(0x4682B4,1.0);
      renderer.setSize(width,height)//设置渲染区尺寸
      renderer.render(scene,camera)//执行渲染操作、指定场景、相机作为参数
      return renderer;
    },

    main(){
      if (!this.camera){
        this.createCamera();
      }
      this.startRenderer();
    },

    /*更新渲染器*/
    updateRenderer(){
      //建模渡船数据
      this.ferry=[];
      this.functionFerry=[];
      this.sessionStorageInfoLoad();
      //靠岸对齐
      this.ferryLanding();

      //接收到请求准备渲染
      let waitDo = this.getWaitLock();

      waitDo(setTimeout(()=>{
        this.isRender = false;
        cancelAnimationFrame(this.frameNum);
        this.isRender = true;
        this.startFrame();
      },200));

    },

    /*页面加载渲染*/
    startRenderer(){
      this.createScene();
      this.createLightAndPushInScene();

      this.destroyThree();


      this.createWater();
      this.createGround();

      /*渲染渡船信息*/
      for (let ferryElement of this.ferry) {
        this.createFerry(ferryElement);
      }

      this.tRender = this.renderer(this.scene,this.camera);

      if (!this.controls){
        this.controls = new OrbitControls(this.camera, this.tRender.domElement)//创建控件对象
        this.controls.screenSpacePanning = true;
      }

      document.getElementById('my-three')?.appendChild(this.tRender.domElement);

      this.startFrame();
    },

    startFrame(){
      if (this.isRender === true){

        this.controls.update();
        /*构建前清除缓存*/
        this.clearC();

        //-------------------


        /*重新构建*/
        this.createScene();
        this.createLightAndPushInScene();
        this.createWater();
        this.createGround();
        /*渲染渡船信息*/
        for (let ferryElement of this.ferry) {
          this.createFerry(ferryElement);
        }
        /*渲染渡船行驶信息*/
        for (let ferryElement of this.functionFerry){
          this.createFerry(ferryElement);
        }

        this.tRender.render(this.scene, this.camera);

        /*设置下一帧行驶渡船移动信息*/
        for (let ferryElement of this.functionFerry){
          /*渡船移动帧*/
          if (ferryElement.y > -20000 ){
            ferryElement.y = ferryElement.y - 10;
          }
        }

        //------------------

        // 使用浏览器自带的请求动画帧函数不断的进行渲染
        this.frameNum = requestAnimationFrame(this.startFrame);
      }
    },

    /*关闭组件*/
    close(){
      cancelAnimationFrame(this.frameNum);
      this.tRender.forceContextLoss();
      this.tRender.content = null;
      this.tRender = null;
      this.camera = null;
      this.scene = null;
    },

    /*清除dom内3D展示*/
    destroyThree(){
      let parent = document.getElementById('my-three');
      let childNodes = parent.childNodes;
      for (let i = childNodes.length - 1; i >= 0; i--) { // 一定要倒序,正序是删不干净的,可自行尝试
        parent.removeChild(childNodes[i]);
      }
    },

    clearC(){
      this.tRender.dispose();
      this.scene.clear();
      THREE.Cache.clear();
      },


    getWaitLock() {
      let lock = true;
      let waitLock = false;

      /*等待锁*/
      let waitDo = (func) => {
        if (lock === false) {
          //上锁
          lock = true;
          func();
        }
        //等待列队中是否存在解锁器
        if (waitLock === false) {
          //不存在则上锁
          waitLock = true;
          //20ms后解锁
          setTimeout(() => {
            if (lock === true) {
              lock = false;
              waitLock = false;
            }
          }, 20);
        }
      }
      return waitDo;
    },

    addKeyDownListener(){
      let _this = this;

      let waitDo = this.getWaitLock();

      document.onkeydown = function(ev) {
        let m = ev || window.event;
        switch (m.keyCode) {

            /*w*/
          case 65:
            //判断clock是否存在
            waitDo(() =>{
              _this.ferry[_this.nFid].x -= 10;
              _this.startRenderer()
            });
            break;

            /*a*/
          case 87:
            //判断clock是否存在
            waitDo(() =>{
              _this.ferry[_this.nFid].y -= 10;
              _this.startRenderer()
            });
            break;

            /*s*/
          case 83:
            //判断clock是否存在
            waitDo(() =>{
              _this.ferry[_this.nFid].y += 10;
              _this.startRenderer()
            });
            break;

            /*d*/
          case 68:
            //判断clock是否存在
            waitDo(() =>{
              _this.ferry[_this.nFid].x += 10;
              _this.startRenderer()
            });
            break;

            /*c*/
          case 67:
            //判断clock是否存在
            waitDo(() =>{
              let length = _this.ferry.length;
              let nFid = _this.nFid;
              if (nFid+1 == length){
                _this.nFid = 0;
              }else {
                _this.nFid +=1;
              }
              _this.startRenderer()
            });
            break;

          /*R暂停渲染*/
          case 82:
            //判断clock是否存在
            waitDo(() =>{
              _this.isRender = _this.isRender === true ? false:true;
              _this.startFrame();
            });
            break;
        }
      }
    }
  }
}


</script>

<style scoped>

canvas {
  align:center
}
</style>

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com

文章标题:ThreeJS

字数:4.3k

本文作者:Os467

发布时间:2022-11-17, 00:07:03

最后更新:2022-11-17, 16:16:31

原始链接:https://os467.github.io/2022/11/17/ThreeJS/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏