近期在开发过程中,因为项目已经接近尾声,就需要对项目中的数据进行整合,而数据看板不失为一个比较直观的展现形式。在数据看板中3D的展现形式是比较流行的展现形式,那么如何在项目引入一个大的场景,并且能够和后台发生交互呢。
我们首先想到的肯定是大型团队都使用的ue4(虚幻)和unity3d,但是在没有个专门的团队的帮助下,谈这些技术都是扯,最后我还是决定使用three.js这个技术,虽然有一定的上手难度,但是最起码前端配合ui就能玩的转一些小的场景。
我使用的是vue3项目的vben框架,如果有不同的前端技术我想差别应该不大。
一、项目位置
我想我们的项目位置在src/components/Scene创建一个vue页面用于展示页面,在需要展示的地方引入该页面即可
二、首先我们来看一下threejs运行的四个条件
scene场景、renderer渲染器、camera相机、object对象在我的项目中是是如何使用的呢
来看具体代码
<script lang="ts" setup>
import { onMounted, onUnmounted, ref, watch } from 'vue';
// 导入场景
import scene from '/@/threes/scene';
//相机
import cameraModule from '/@/threes/camera';
// 导入控制器
import controls from '/@/threes/controls';
// 导入辅助坐标轴
import axesHelper from '/@/threes/axesHelper';
// 导入渲染器
import renderer from '/@/threes/renderer';
// 初始化调整屏幕
import '/@/three/init';
// 导入添加物体函数
import createMesh from '/@/threes/createMesh';
// 导入每一帧的执行函数
import animate from '/@/threes/animate';
//绑定交互事件
import eventHub from '/@/utils/eventHub';
// 场景元素div
let sceneDiv = ref(null);
// 添加相机
scene.add(cameraModule.activeCamera);
// 添加辅助坐标轴
// scene.add(axesHelper);
// 创建物体
createMesh();
// 创建事件的问题
const props = defineProps(['eventList']);
const onmousedown = (e) => {
eventHub.emit('mouseDown', e);
};
onMounted(() => {
sceneDiv.value.appendChild(renderer.domElement);
addEventListener('click', onmousedown, false);
animate();
});
watch(
() => props.eventList,
() => {
console.log('触发事件列表更新2');
eventHub.emit('setEventList', props.eventList);
},
{ deep: true },
);
onUnmounted(() => {
console.log('销毁');
removeEventListener('click', onmousedown, false);
// removeEventListener('mousedown', onmousedown, false);
});
</script>
scene.js文件主要创建了一个场景,并添加了平行光,当然天气等条件都能加入进来
import * as THREE from 'three';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
// 初始化场景
const scene = new THREE.Scene();
// 添加雾霾
// const fog = new THREE.Fog(0x000000, 0, 1000);
// scene.fog = fog;
// 导入hdr纹理
const hdrLoader = new RGBELoader();
hdrLoader.loadAsync('./textures/023.hdr').then((texture) => {
scene.background = texture;
scene.environment = texture;
scene.environment.mapping = THREE.EquirectangularReflectionMapping;
});
// 添加平行光
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(10, 100, 10);
scene.add(light);
export default scene;
cameraModule.js文件主要设置了相机的位置,新增相机和切换相机的方法,当然这个的命名是根据你的场景里相机的位置来决定的
import * as THREE from 'three';
import eventHub from '/@/utils/eventHub';
// 创建透视相机
const camera = new THREE.PerspectiveCamera(75, window.innerHeight / window.innerHeight, 1, 100000);
// 设置相机位置
camera.position.set(10, 80, 0);
class CameraModule {
constructor() {
this.activeCamera = camera;
this.collection = {
default: camera,
};
eventHub.on('toggleCamera', (name) => {
this.setActive(name);
});
}
add (name, camera) {
this.collection[name] = camera;
}
setActive (name) {
this.activeCamera = this.collection[name];
}
}
export default new CameraModule();
renderer.js渲染器主要是设置了webGL的一些参数,使你的场景不那么假
import * as THREE from 'three';
// 初始化渲染器
const renderer = new THREE.WebGLRenderer({
// 设置抗锯齿
antialias: true,
// depthbuffer
logarithmicDepthBuffer: true,
physicallyCorrectLights: true,
alpha: true,
});
// 设置渲染尺寸大小
// renderer.setSize(window.innerWidth, window.innerHeight);
setTimeout(() => {
const container = document.getElementById('sceneDiv');
renderer.setSize(container.offsetWidth, container.offsetHeight);
renderer.setClearAlpha(0);
renderer.shadowMap.enabled = true;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.5;
}, 40);
export default renderer;
我的object对象的加载放在了createMesh.js这个文件里面,能够让分清他们的职责。
import scene from './scene';
import City from './mesh/City';
let city;
export default function createMesh () {
// 创建城市
city = new City(scene);
}
export function updateMesh (time) {
city.update(time);
}
当然在city.js里面我也加入了加载的方法,能让public的glb数据文件能够加载到场景中。
this.loader.load('./screen/earth.glb', (gltf) => {
scene.add(gltf.scene);
// targetList.push(gltf.scene);
// 场景子元素遍历
this.gltf = gltf;
this.floor1Group = gltf.scene;
});
好了,基本的数据结构就是这样的,那么就让我们来看看实际的效果图吧
希望对大家学习使用three.js有用,有什么不懂的都可以问我哦