import { handleLClick } from "./handleRLclick/handleLclick"; import { handleRClick } from "./handleRLclick/handleRclick"; import { handleDBLClick } from "./handleDBLclick"; import { checkAnimation } from "./checkTag"; import { handleEndChange, handleStartChange, } from "./handleOrbitControlsChange"; import { saveState, resetState } from "./viewBack"; import addFunction from "./addEvent"; import { editTunnelInit } from "./editTunnelInit"; import { addEquipment, removeEquipment } from "./editEquipment"; import previewtunnelModeInit from "./previewTunnelInit" export default class Demo { // 摄像机看向位置 origin = null; //附着点的模型 points = []; //设备模型数组 deviceModels = []; constructor(three, mountedElement) { this._handleLClick = handleLClick; this._handleRClick = handleRClick; this._handleDBLClick = handleDBLClick; this._checkAnimation = checkAnimation; this._saveState = saveState; this._resetState = resetState; this.addEquipment = addEquipment; this.removeEquipment = removeEquipment; this.previewtunnelModeInit = previewtunnelModeInit; this.editTunnelInit = editTunnelInit; // 外部可添加函数 this.addFunction = addFunction; this.THREE = three; this.mountedElement = mountedElement; //初始化场景 this.scene = new this.THREE.Scene(); // 初始化摄像机 this.camera = new this.THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 ); this.camera.position.z = 1.24; this.camera.position.y = 9.14; this.camera.position.x = -63.79; this.camera.lookAt(0, 0, 1000); // 初始化渲染器 this.renderer = new this.THREE.WebGLRenderer({ // 抗锯齿 antialias: true, }); this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.domElement.style.position = "absolute"; this.mountedElement.appendChild(this.renderer.domElement); // 创建渲染帧 this.render = () => { this.__renderScope(); requestAnimationFrame(this.render); }; this.render(); this.canvasResize(); this.addXYZ(); } //渲染函数作用域(这里主要写渲染帧内操作) __renderScope() { this.renderer.render(this.scene, this.camera); // 轨道控制器更新 if (this.orbitControls) { this.orbitControls.update(); // console.log(this.camera.position); } //html标签渲染 if (this.CSS2Renderer) { this.CSS2Renderer.render(this.scene, this.camera); } // 选中标签动画渲染 if (this.checkAnimationTween) { this.checkAnimationTween.update(); } // 视角重置动画 if (this.resetViewAngleAnimation) { this.resetViewAngleAnimation.update(); } //视角回退动画 if (this.viewSateResetAnimation) { this.viewSateResetAnimation.update(); } // 视角跟随动画 if (this.intoPointAnimation) { this.intoPointAnimation.update(); } // 隧道颜色渐变动画 if (this.opacityTween) { this.opacityTween.update(); } // 双击进入动画 if (this.dblIntoTween) { this.dblIntoTween.update(); } // 风机叶片旋转动画 if (this.fanSpinArray && this.fanSpinArray.length) { this.fanSpinArray.forEach((element) => element.update()); } } // 添加世界坐标系 addXYZ() { // const axesHelper = new this.THREE.AxesHelper(100); // this.scene.add(axesHelper); } // 屏幕自适应 canvasResize() { window.addEventListener("resize", () => { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); this.CSS2Renderer.setSize(window.innerWidth, window.innerHeight); }); } // 模型加载 /** * @param {Object} GLTFLoader 模型加载器 * @param {String} path 模型资源路径 */ loadModel(GLTFLoader, path) { return new Promise((resolve, reject) => { this.gltfloader = new GLTFLoader(); this.gltfloader.load( "/tunnelModel/chanel.gltf", (gltf) => { gltf.scene.traverse((child) => { this._forModels(child); }); // 初始化场景位置 gltf.scene.position.set(-5, 0, 10); this.scene.add(gltf.scene); // 加载完后可执行函数 this._afterLoaded(gltf.scene); resolve(gltf); }, (xhr) => { console.log((xhr.loaded / xhr.total) * 100 + "% loaded"); }, (error) => { console.log(error); reject(error); } ); }); } // 模型加载完函数 _afterLoaded() { // 可以进行选中 this._hoverModel(this.points); // 可以进行点击 this._ClickModel(this.points); } // 在此方法中对模型批量操作,这里遍历附着点 _forModels(child) { // 匹配附着点(这里不适合对单个模型进行保存,批量对模型进行操作) if (child.isMesh && /^point+/.test(child.name)) { this.points.push(child); // 改变为基础材质 child.material = new this.THREE.MeshBasicMaterial(); child.scale.set(0.01, 0.01, 0.01); if (/b[rl]$/.test(child.name)) child.translateZ(2); // 遍历一个属性是否存在设备 child.hasDevice = false; //初始化 } } // 添加轨道控制器 /** * @param {Object} OrbitControls 轨道控制器 */ addOrbitControls(OrbitControls) { this.orbitControls = new OrbitControls( this.camera, this.renderer.domElement ); //初始化轨道控制器 this.orbitControls.object.position.set(-29, 18, -50); this.orbitControls.maxPolarAngle = (4 * Math.PI) / 7; this.orbitControls.minDistance = 5; this.orbitControls.maxDistance = 87; // 监听控制器变化 this.orbitControls.addEventListener("end", handleEndChange.bind(this)); this.orbitControls.addEventListener("start", handleStartChange.bind(this)); } /** * @param {Class} Tween 添加补间动画 */ addTween(Tween) { this.TWEEN = Tween; } /*** * 鼠标移入可以高亮的模型 *@param {Array} hoverModels 需要高亮的模型 */ _hoverModel(hoverModels) { this.renderer.domElement.addEventListener("mousemove", (e) => { const mouse = new this.THREE.Vector2(); mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; const raycaster = new this.THREE.Raycaster(); raycaster.setFromCamera(mouse, this.camera); const intersects = raycaster.intersectObjects(hoverModels); if (intersects.length > 0) { if (!this.preHover) { this.preHover = intersects[0].object; } else { this.preHover.material.color.set("white"); this.preHover = intersects[0].object; } intersects[0].object.material.color.set("red"); const tagP = intersects[0].object.getWorldPosition( new this.THREE.Vector3() ); this._checkAnimation(tagP); } else { if (!this.preHover) return; this.preHover.material.color.set("white"); } }); } /** * @param {Array} isClickModels 可以进行点击的模型吧 */ _ClickModel(isClickModels) { // 点击两次处理事件 this.renderer.domElement.addEventListener( "dblclick", this.__handleListenerDBLClickEvent.bind(this, isClickModels) //注意这里重新指向this,第一个是修改this指向,第二个是传入函数的参数 ); // 点击一次时处理时间 this.renderer.domElement.addEventListener( "mousedown", this.__handleListenerClickRLEvent.bind(this, isClickModels) //注意这里重新指向this,第一个是修改this指向,第二个是传入函数的参数 ); } // 处理点击2次触发事件事件 __handleListenerDBLClickEvent(isClickModels, e) { e.preventDefault(); // 如果监听到双击则清空单次点击事件 clearTimeout(this.EventTimer); //最后一个是监听器默认参数 const mouse = new this.THREE.Vector2(); mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; const raycaster = new this.THREE.Raycaster(); raycaster.setFromCamera(mouse, this.camera); const intersects = raycaster.intersectObjects(isClickModels); // 取消定位的视角返回初始化 if (intersects.length === 0) { this.clearTagsObj(); this._resetState(); return; } // 处理连续双击击事件 this._handleDBLClick(intersects[0].object); } // 处理点击左右键触发的事件 __handleListenerClickRLEvent(isClickModels, e) { e.preventDefault(); clearTimeout(this.EventTimer); this.EventTimer = setTimeout(() => { const mouse = new this.THREE.Vector2(); mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; const raycaster = new this.THREE.Raycaster(); raycaster.setFromCamera(mouse, this.camera); const intersects = raycaster.intersectObjects(isClickModels); if (intersects.length === 0) return; //附着点设置方框 this.setBoxHelper(intersects[0].object); // 处理点击左右键事件 if (e.button === 0) { this._handleLClick(intersects[0].object); } else if (e.button === 2) { this._handleRClick(intersects[0].object); } }, 400); } // 添加css3Renderer addCSS3Renderer(CSS2Renderer, CSS2DObject, [infoDom, editDom]) { this.CSS2Renderer = new CSS2Renderer(); this.CSS2Renderer.setSize(window.innerWidth, window.innerHeight); this.CSS2Renderer.render(this.scene, this.camera); this.mountedElement.appendChild(this.CSS2Renderer.domElement); // 初始化html标签 this.tagHtml = infoDom; this.tag2Html = infoDom; this.tag3Html = editDom; this.CSS2DObject = CSS2DObject; // 创建html标签模型 this.tagCSS2DObj = new CSS2DObject(this.tagHtml); this.tag2CSS2DObj = new CSS2DObject(this.tag2Html); this.tag3CSS2DObj = new CSS2DObject(this.tag3Html); this.tagCSS2DObj.element.style.display = "none"; this.tag2CSS2DObj.element.style.display = "none"; this.tag3CSS2DObj.element.style.display = "none"; // // 设置该标签初始化透明 // this.tagCSS2DObj.element.style.opacity = "1"; // this.tag2CSS2DObj.element.style.opacity = "1"; this.tagCSS2DObj.scale.set(0.1, 0.1, 0.1); this.tag3CSS2DObj.scale.set(0.02, 0.02, 0.02); //编辑框 this.tag3CSS2DObj.position.set(10, 0, 10); } clearTagsObj() { if (this.preDBLModel) { this.preDBLModel.remove(this.tagCSS2DObj); } // 所有标签看不见 this.scene.remove(this.tag2CSS2DObj); this.scene.remove(this.tag3CSS2DObj); this.tag3CSS2DObj.element.style.opacity = "0"; // 删除标记动画 this.scene.remove(this.group); } // 附着点选中线宽包围 setBoxHelper(obj) { // 判断场景是否纯在该3d对象 if (this.scene.getObjectByName("boxHelper")) { const preModel = this.scene.getObjectByName("boxHelper"); this.scene.remove(preModel); } const boxHelper = new this.THREE.BoxHelper(obj, 0xffff00); boxHelper.name = "boxHelper"; this.scene.add(boxHelper); } /** *@param {Class} DRACOLoader 解压器 * @param {String} dracoPath 解压器路径 */ addDARCOLoder(DRACOLoader, dracoPath) { this.DRAC = new DRACOLoader(); this.DRAC.setDecoderPath(dracoPath); //给模型加载器添解压器 this.gltfloader.setDRACOLoader(this.DRAC); } /** * @param {Map} meshes gltf加载过后的模型Map */ initDevicesModel(equMap) { // 把风机叶片做为一个组,方便后期旋转 const group = new this.THREE.Group(); group.name = "fanLeaf"; // 初始化风机 this.equMap = equMap; // 初始化风机颜色 this.equMap.get("equ_fan").traverse((v) => { v.material = new this.THREE.MeshBasicMaterial(); v.material.color = new this.THREE.Color(0xC0C0C0); if (/^leaf/.test(v.name) || /^roller/.test(v.name)) { group.add(v.clone()); v.visible = false; // console.log(v); } }); // const axesHelper = new this.THREE.AxesHelper(100); // 改变叶子旋转中心 let x = 0, y = -0.2099, z = 0; const wrapper = new this.THREE.Object3D(); wrapper.position.set(x, y, z); wrapper.add(group); group.position.set(-x, -y, -z); // wrapper.add(axesHelper); wrapper.rotation.z = Math.PI / 4; wrapper.name = "fan_leafs"; this.equMap.get("equ_fan").add(wrapper); // 初其他传感器机颜色 this.equMap.get("equ_sensors").scale.set(0.2, 0.2, 0.2); this.equMap.get("equ_sensors").traverse((v) => { v.material = new this.THREE.MeshBasicMaterial(); v.material.color = new this.THREE.Color("white"); }); // 初始标签面板 const tag = new this.THREE.Mesh( new this.THREE.PlaneGeometry(7, 5), new this.THREE.MeshBasicMaterial({ color: "white" }) ); this.equMap.get("equ_sensors").rotation.x += Math.PI / 2; this.equMap.get("equ_sensors").add(tag); tag.name = "tag"; tag.rotation.x = Math.PI / 2; tag.rotation.z = Math.PI / 2; //旋转这里改变文字顺序 tag.rotation.y = Math.PI; tag.rotation.z -= Math.PI / 2; tag.translateZ(2.0); } /** * @param {String} devId 删除的设备ID */ removeDevice(devId) { removeDev.bind(this, devId); } /** * * @param {Number} distance 设置锚点之间的间隔距离 */ setDistance(distance = 10) { this.distance = distance; } loadBackground(hdrLoader, backColorSet) { this.scene.background = new this.THREE.TextureLoader().load( "/images/background/background.png", function (texture) { texture.encoding = backColorSet; } ); } /** * * @param {Boolean} option 是否禁用轨道控制器 */ isControlOrbit(option = true) { this.orbitControls.enabled = option; const mesh = this.scene.getObjectByName("chanel"); const mesh2 = this.scene.getObjectByName("chanel_1"); const mesh3 = this.scene.getObjectByName("chanel_2"); const mesh4 = this.scene.getObjectByName("chanel_3"); const opacityTween = (startVal, endVal, isNeedLookAt = false) => { this.opacityTween = new this.TWEEN.Tween({ opacity: startVal, lookX: this.orbitControls.target.x, lookY: this.orbitControls.target.y, lookZ: this.orbitControls.target.z, }); this.opacityTween.to( { opacity: endVal, lookX: 0, lookY: 0, lookZ: 0, }, 500 ); this.opacityTween.start(); this.opacityTween.onUpdate((obj) => { // mesh.material.opacity = obj.opacity; // mesh2.material.opacity = obj.opacity; // mesh3.material.opacity = obj.opacity; // mesh4.material.opacity = obj.opacity; if (!isNeedLookAt) return; this.camera.lookAt(obj.lookX, obj.lookY, obj.lookZ); this.orbitControls.target.set(obj.lookX, obj.lookY, obj.lookZ); }); }; if (!option) { // 显示编辑框隧道颜色 opacityTween(0.5, 0.1); } else { // 退出编辑框什么颜色 opacityTween(0.1, 0.5, true); } } ThreeConfig = { code: 0, data: { tunnelThreeConfig: [{ equipmentId: 'fan_01',//传感器id equipmentName: '01',//设备名称 pointName: 'point_005_tl',//附着点名称(定位) equipmentType: 'fan',//设备类型(类型可根据后端 equipmentValue: 23, //设备存的值 }, { equipmentId: 'sensors_01',//传感器id equipmentName: '01',//设备名称 pointName: 'point_009_bl',//附着点名称(定位) equipmentType: 'sensors',//设备类型(类型可根据后端 equipmentValue: 67, //设备存的值 }], }, msg: "dda" } tunnelConfigEquipment = this.ThreeConfig.data.tunnelThreeConfig }