Files
datav---Cattle-Industry/src/utils/Earth.js

303 lines
9.4 KiB
JavaScript
Raw Normal View History

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import Stats from 'three/examples/jsm/libs/stats.module';
import TWEEN from '@tweenjs/tween.js';
import { deepMerge, isType } from '@/utils';
export default class Earth3d {
constructor(options = {}) {
let defaultOptions = {
isFull: true,
container: null,
width: window.innerWidth,
height: window.innerHeight,
2025-11-26 17:31:42 +08:00
bgColor: 0x0e2438, // 深蓝偏亮背景,替代纯黑以提升亮度
materialColor: 0xff0000,
controls: {
visibel: true, // 是否开启
enableDamping: true, // 阻尼
autoRotate: false, // 自动旋转
maxPolarAngle: Math.PI, // 相机垂直旋转角度的上限
},
statsVisibel: true,
axesVisibel: true,
axesHelperSize: 250, // 左边尺寸
};
this.options = deepMerge(defaultOptions, options);
this.container = document.querySelector(this.options.container);
// 确保容器尺寸有效避免Canvas尺寸为0的错误
this.options.width = Math.max(this.container.offsetWidth, 800);
this.options.height = Math.max(this.container.offsetHeight, 600);
// 如果容器尺寸仍然为0使用默认值
if (this.options.width === 0 || this.options.height === 0) {
this.options.width = 800;
this.options.height = 600;
}
this.scene = new THREE.Scene(); // 场景
this.camera = null; // 相机
this.renderer = null; // 渲染器
this.mesh = null; // 网格
this.animationStop = null; // 用于停止动画
this.controls = null; // 轨道控制器
this.stats = null; // 统计
this.init();
}
init() {
this.initStats();
this.initCamera();
this.initModel();
this.initRenderer(); // 异步初始化其他组件在continueInit中初始化
}
async initModel() {}
/**
* 运行
*/
run() {
// 如果渲染器已经准备好,直接开始循环
if (this.renderer) {
this.loop();
} else {
// 否则标记需要启动,等待渲染器创建完成
this.shouldStart = true;
}
}
// 循环
loop() {
// 检查渲染器是否存在
if (!this.renderer) {
return;
}
this.animationStop = window.requestAnimationFrame(() => {
this.loop();
});
// 这里是你自己业务上需要的code
this.renderer.render(this.scene, this.camera);
// 控制相机旋转缩放的更新
if (this.options.controls.visibel) this.controls.update();
// 统计更新
if (this.options.statsVisibel) this.stats.update();
TWEEN.update();
}
initCamera() {
let { width, height } = this.options;
let rate = width / height;
// 设置45°的透视相机,更符合人眼观察
this.camera = new THREE.PerspectiveCamera(45, rate, 0.1, 1500);
// this.camera.position.set(-428.88, 861.97, -1438.0)
this.camera.position.set(270.27, 173.24, 257.54);
// this.camera.position.set(-102, 205, -342)
this.camera.lookAt(0, 0, 0);
}
/**
* 初始化渲染器
*/
initRenderer() {
let { width, height, bgColor } = this.options;
// 强制清理所有WebGL上下文
if (this.renderer) {
this.renderer.dispose();
this.renderer.forceContextLoss();
this.renderer = null;
}
// 清理容器中现有的所有子元素
while (this.container.firstChild) {
this.container.removeChild(this.container.firstChild);
}
// 强制垃圾回收
if (window.gc) {
window.gc();
}
// 延迟创建新的渲染器,确保上下文完全释放
setTimeout(() => {
try {
// 重新获取容器尺寸,确保有效
const containerWidth = this.container.offsetWidth || window.innerWidth;
const containerHeight = this.container.offsetHeight || window.innerHeight;
// 确保尺寸有效
const validWidth = Math.max(containerWidth, 800);
const validHeight = Math.max(containerHeight, 600);
// 更新options中的尺寸
this.options.width = validWidth;
this.options.height = validHeight;
// 创建一个新的canvas元素
const canvas = document.createElement('canvas');
let renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
preserveDrawingBuffer: false,
powerPreference: "high-performance"
});
// 设置canvas的分辨率
renderer.setPixelRatio(window.devicePixelRatio);
// 设置canvas 的尺寸大小
renderer.setSize(validWidth, validHeight);
// 设置背景色
renderer.setClearColor(bgColor, 1);
// 设置canvas的z-index确保CSS2D渲染器在其之上
renderer.domElement.style.zIndex = '1';
renderer.domElement.style.position = 'absolute';
// 插入到dom中
this.container.appendChild(renderer.domElement);
this.renderer = renderer;
// 继续初始化其他组件
this.continueInit();
} catch (error) {
console.error('WebGL渲染器创建失败:', error);
// 创建一个错误提示
const errorDiv = document.createElement('div');
errorDiv.innerHTML = '3D渲染器初始化失败请刷新页面重试';
errorDiv.style.cssText = 'color: #ff6b6b; text-align: center; padding: 50px; font-size: 16px;';
this.container.appendChild(errorDiv);
}
}, 100);
}
continueInit() {
// 原来在init方法中renderer初始化后的逻辑
this.initLight();
this.initStats();
this.initControls();
this.initAxes();
// 如果之前调用了run方法现在启动循环
if (this.shouldStart) {
this.shouldStart = false;
this.loop();
}
}
initLight() {
// 平行光1
let directionalLight1 = new THREE.DirectionalLight(0xffffff, 0.6);
directionalLight1.position.set(400, 200, 200);
// 平行光2
let directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.6);
directionalLight2.position.set(-400, -200, -300);
// 环境光
let ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
// 将光源添加到场景中
this.addObject(directionalLight1);
this.addObject(directionalLight2);
this.addObject(ambientLight);
}
initStats() {
if (!this.options.statsVisibel) return false;
// 确保容器有有效的尺寸
if (!this.container || this.container.offsetWidth === 0 || this.container.offsetHeight === 0) {
console.warn('Container not ready for stats initialization, skipping...');
return false;
}
this.stats = new Stats();
// 确保stats的DOM元素有正确的尺寸
if (this.stats.dom) {
this.stats.dom.style.position = 'absolute';
this.stats.dom.style.top = '0px';
this.stats.dom.style.left = '0px';
this.stats.dom.style.zIndex = '100';
}
this.container.appendChild(this.stats.dom);
}
initControls() {
try {
let {
controls: { enableDamping, autoRotate, visibel, maxPolarAngle },
} = this.options;
if (!visibel) return false;
// 轨道控制器,使相机围绕目标进行轨道运动(旋转|缩放|平移)
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.maxPolarAngle = maxPolarAngle;
this.controls.autoRotate = autoRotate;
this.controls.enableDamping = enableDamping;
} catch (error) {
console.log(error);
}
}
initAxes() {
if (!this.options.axesVisibel) return false;
var axes = new THREE.AxesHelper(this.options.axesHelperSize);
this.addObject(axes);
}
// 清空dom
empty(elem) {
while (elem && elem.lastChild) elem.removeChild(elem.lastChild);
}
/**
* 添加对象到场景
* @param {*} object {} []
*/
addObject(object) {
if (isType('Array', object)) {
this.scene.add(...object);
} else {
this.scene.add(object);
}
}
/**
* 移除对象
* @param {*} object {} []
*/
removeObject(object) {
if (isType('Array', object)) {
object.map((item) => {
item.geometry.dispose();
});
this.scene.remove(...object);
} else {
object.geometry.dispose();
this.scene.remove(object);
}
}
/**
* 重置
*/
resize() {
// 重新设置宽高
let newWidth = this.container.offsetWidth || window.innerWidth;
let newHeight = this.container.offsetHeight || window.innerHeight;
// 确保尺寸有效
this.options.width = Math.max(newWidth, 800);
this.options.height = Math.max(newHeight, 600);
if (this.renderer) {
this.renderer.setSize(this.options.width, this.options.height);
}
// 重新设置相机的位置
let rate = this.options.width / this.options.height;
// 必須設置相機的比例,重置的時候才不会变形
this.camera.aspect = rate;
// 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
// 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
// 如果相机的一些属性发生了变化需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
this.camera.updateProjectionMatrix();
// 如果stats还没有初始化可能之前容器尺寸为0现在重新尝试初始化
if (this.options.statsVisibel && !this.stats) {
this.initStats();
}
}
}