Three.js - 封装
对three.js 的封装,建议使用ES6 Class
构造函数
在构造函数中实例化各种对象
javascript
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; //导入轨道控制器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
import {
CSS2DRenderer,
CSS2DObject,
} from "three/addons/renderers/CSS2DRenderer.js";
export class ThreeScene {
constructor(width, height, container) {
// Step1. 实例化scene、camera、render、controls等对象
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 1000);
this.renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
});
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
// 这两个数组,用于保存模型与2D标签
this.models = [];
this.labels = [];
// Step2. 实例化标签容器
this.labelRenderer = new CSS2DRenderer();
this.labelRenderer.setSize(width, height);
this.labelRenderer.render(this.scene, this.camera);
this.labelRenderer.domElement.style.position = "absolute";
this.labelRenderer.domElement.style.top = "0";
this.labelRenderer.domElement.style.left = "0";
this.labelRenderer.domElement.style.pointerEvents = "none";
container.appendChild(this.labelRenderer.domElement);
// Step3. 初始化光源
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.4);
directionalLight.position.set(30, 10, 20);
this.scene.add(directionalLight);
// Step4. 将 three.js的 DOM节点添加至指定节点下
container.appendChild(this.renderer.domElement);
// 构造函数结束
}
}
加载模型与标签
javascript
export class Scene {
constructor(width, height, container) {
// 初始化
}
/**
* @name loadModel
* @description 加载模型
* @param {String} path 模型文件所在路径(必须在public文件夹下)
* @param {Function} callback (可选)加载完成后的回调,参数为Three.js模型实例
*/
loadModel = (path, callback) => {
loader.load(path, (gltf) => {
this.scene.add(gltf.scene);
this.models.push(gltf.scene);
callback && callback(gltf.scene);
});
};
/**
* @name addLabel
* @description 在指定模型上添加标签
* @param {VNode} component 标签渲染的Vue组件实例
* @param {THREE.Object3D} model Three.js模型
* @param {Number} x x坐标
* @param {Number} y y坐标
* @param {Number} z z坐标
*/
addLabel = (component, model, x, y, z) => {
let label = new CSS2DObject(component);
// 设置标签位置
label.position.set(x, y, z);
// 将标签添加到目标模型上
model.add(label);
this.labels.push(label);
};
}
渲染、销毁、调整大小
javascript
export class Scene {
constructor(width, height, container) {
// 初始化
}
resize = (width, height) => {
this.renderer.setSize(width, height);
this.labelRenderer.setSize(width, height);
this.camera.aspect = width / height;
this.camera.updateProjectionMatrix();
this.renderer.setPixelRatio(window.devicePixelRatio);
};
render = (func) => {
const anime = () => {
if (!this.renderer) {
return;
}
this.labelRenderer?.render(this.scene, this.camera);
this.controls.update();
this.renderer.render(this.scene, this.camera);
// 加了个回调
func &&
func({
scene: this.scene,
camera: this.camera,
renderer: this.renderer,
controls: this.controls,
models: this.models,
labels: this.labels,
});
requestAnimationFrame(anime);
}
anime();
};
destroy = () => {
try {
// 销毁Three.js实例
renderer.dispose();
renderer.forceContextLoss();
renderer.content = null;
let gl = renderer.domElement.getContext("webgl");
if (gl && gl.getExtension("WEBGL_lose_context")) {
gl.getExtension("WEBGL_lose_context").loseContext();
}
renderer = null;
camera = null;
scene.traverse((child) => {
if (child.material) {
child.material.dispose();
}
if (child.geometry) {
child.geometry.dispose();
}
child = null;
});
scene = null;
} catch (e) {
console.error("Failed to destroy three.js instance", e);
}
};
}