现在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;