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, 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(); } } }