现在vue3的项目中,大部分是vite+vue3+typescript+pinia的技术栈
vue3项目中安装three.js
pnpm i @types/three three
注意:在package.json中查看他们的版本,如果版本不一致的话,可能导致ts不能识别three这个模块
导入three模块
import * as THREE from "three";import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
一些three的插件在examples/jsm的目录下
使用模型文件
在vite项目下,静态资源放在public目录下,特别是经过压缩过的glb模型文件,要使用three的draco去解码,
在官方的例子里,一些代码放在跟例子一个js目录里,安装的three.js里没有自带,所有需要复制到public里调用。
使用类创建three
const three = reactive<{ base3d?: Base3D }>({});onMounted(async () => {three.base3d = new Base3D("#my_parking", 45, 0.1, 500);
});
onMounted中才能获取DOM结点
附上一份自己封装的three类,方便快速创建实例
import * as THREE from "three";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
class Base3D {container: Element | null;camera: THREE.PerspectiveCamera;scene: THREE.Scene;renderer: THREE.WebGLRenderer;controls: OrbitControls;animate?: () => void;//构造函数constructor(selector: string,fov=70,near=0.1,far=200) {this.scene = new THREE.Scene();this.camera = new THREE.PerspectiveCamera(fov,window.innerWidth / window.innerHeight,near, //最近能看到的距离far);this.camera.updateProjectionMatrix();// this.camera.position.set(1, 1, 1);this.scene.add(this.camera);this.renderer = new THREE.WebGLRenderer({ antialias: true });this.renderer.setPixelRatio(window.devicePixelRatio);this.renderer.outputEncoding = THREE.sRGBEncoding;this.renderer.toneMapping = THREE.ACESFilmicToneMapping;this.renderer.toneMappingExposure = 0.85;this.renderer.setSize(window.innerWidth, window.innerHeight);this.renderer.setAnimationLoop(this.render.bind(this));this.controls = new OrbitControls(this.camera, this.renderer.domElement);//设置阻尼效果this.controls.enableDamping = true;//监听屏幕大小改变,修改渲染器的宽高,相机的比例window.addEventListener("resize", () => {this.camera.aspect = window.innerWidth / window.innerHeight;this.camera.updateProjectionMatrix();//设置渲染器宽高this.renderer.setSize(window.innerWidth, window.innerHeight);});this.container = document.querySelector(selector);if (!this.container) return;this.container?.appendChild(this.renderer.domElement);}setSceneBg() {this.scene.background = new THREE.Color(0x444444);// this.scene.environment = new RGBELoader().load(// "textures/equirectangular/venice_sunset_1k.hdr"// );// this.scene.environment &&// (this.scene.environment.mapping = THREE.EquirectangularReflectionMapping);}render() {//添加动画this.animate && this.animate();this.controls.update();this.renderer.render(this.scene, this.camera);}add(...object: THREE.Object3D<THREE.Event>[]) {this.scene.add(...object);}loadGLTF(name: string) {const dracoLoader = new DRACOLoader();dracoLoader.setDecoderPath("js/libs/draco/gltf/");const loader = new GLTFLoader();loader.setDRACOLoader(dracoLoader);return new Promise((relove, reject) => {loader.setPath("object/").load(name,(gltf) => {this.add(gltf.scene);relove("");},undefined,() => {reject("错误!");});});}
}export default Base3D;