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