123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932 |
- <!-- 模型地图 -->
- <template>
- <div>
- <div class="canvasMap" ref="container"></div>
- </div>
- </template>
- <script>
- import * as THREE from 'three';
- import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
- import TWEEN from 'tween.js';
- import { CSS2DRenderer, CSS2DObject, } from "three/examples/jsm/renderers/CSS2DRenderer";
- export default {
- data() {
- return {
- animationId: null, // 存储动画帧ID
- model:null,
- rotateType:true,
- subModels: [],
- houses:[],
- textures: [], // 预加载的贴图选项
- scene: null,
- camera: null,
- renderer: null,
- controls: null,
- labelRenderer:null,
- // initialCameraPosition: new THREE.Vector3(0.265357272970017,1.0753467424793826,0.29965000584249346),//旋转-距离-俯角
- initialCameraPosition: new THREE.Vector3(0.5,0.60,0.5),//旋转-距离-俯角
- /*
- {"x":0.2509298883560245,"y":1.2088432847403026,"z":0.19985043873282318}
- {"x":0.265357272970017,"y":1.0753467424793826,"z":0.29965000584249346}
- */
- initialControlsTarget: new THREE.Vector3(0, 0, 0),
- /*本地管理数据*/
- modelBuildingList:[
- {school:'B', name:'5号楼', buildId:'101093843117592833',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'6B教学楼', buildId:'101093843117592815',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'中心实验楼', buildId:'101093843117592829',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'信息工程学院教学楼', buildId:'101093843117592812',centerValue:'101093843117592812',valueList:['101093843117592812'],alarmType:false,},
- {school:'B', name:'养虫楼', buildId:'101093843117592805',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'农业机械实验室平房', buildId:'101093843117592807',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'农药研究所', buildId:'101093843117592804',centerValue:'101093843117592804',valueList:['101093843117592804'],alarmType:false,},
- {school:'B', name:'动物中心B座', buildId:'101093843117592785',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'动物中心实验楼A', buildId:'101093843117592765',centerValue:'101093843117592765',valueList:['101093843117592765'],alarmType:false,},
- {school:'B', name:'动物中心实验楼B', buildId:'101093843117592764',centerValue:'101093843117592764',valueList:['101093843117592764'],alarmType:false,},
- {school:'B', name:'动物科技学院楼', buildId:'101093843117592831',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'动科楼', buildId:'101093843117592832',centerValue:'101093843117592832',valueList:['101093843117592832'],alarmType:false,},
- {school:'B', name:'北1号教学楼', buildId:'101093843117592802',centerValue:'101093843117592802',valueList:['101093843117592802'],alarmType:false,},
- {school:'B', name:'北2号教学楼', buildId:'101093843117592813',centerValue:'101093843117592813',valueList:['101093843117592813'],alarmType:false,},
- {school:'B', name:'北3号教学楼', buildId:'101093843117592782',centerValue:'101093843117592782',valueList:['101093843117592782'],alarmType:false,},
- {school:'B', name:'北4号教学楼', buildId:'101093843117592781',centerValue:'101093843117592781',valueList:['101093843117592781'],alarmType:false,},
- {school:'B', name:'北5号教学楼', buildId:'101093843117592783',centerValue:'101093843117592833',valueList:['101093843117592833'],alarmType:false,},
- {school:'B', name:'北6号教学楼', buildId:'101093843117592814',centerValue:'101093843117592814',valueList:['101093843117592814'],alarmType:false,},
- {school:'B', name:'北7号教学楼', buildId:'101093843117592816',centerValue:'101093843117592816',valueList:['101093843117592816'],alarmType:false,},
- {school:'B', name:'危化品服务中心', buildId:'101093843117592826',centerValue:'101093843117592826-03',valueList:['101093843117592826-01','101093843117592826-02','101093843117592826-03','101093843117592826-04','101093843117592826-05'],alarmType:false,},
- {school:'B', name:'原农一站办公楼', buildId:'101093843117592811',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'工程训练中心楼', buildId:'101093843117592808',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'教学办公楼', buildId:'101093843117592806',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'未知实验室 ',buildId:'1855791848753147906',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'机械及液压实验室平房', buildId:'101093843117592810',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'机电锻铸车间', buildId:'101093843117592809',centerValue:'101093843117592809',valueList:['101093843117592809'],alarmType:false,},
- {school:'B', name:'水利与建筑工程学院', buildId:'101093843117592788',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'水利与建筑工程学院A', buildId:'101093843117592766',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'水利与建筑工程学院B', buildId:'101093843117592768',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'水利与建筑工程学院C', buildId:'101093843117592769',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'水利与建筑工程学院D', buildId:'101093843117592767',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'水工厅', buildId:'1871736666456633346',centerValue:'1871736666456633346',valueList:['1871736666456633346'],alarmType:false,},
- {school:'B', name:'水工水力学实验大厅', buildId:'1871729400227606529',centerValue:'1871729400227606529-01',valueList:['1871729400227606529-01','1871729400227606529-02'],alarmType:false,},
- {school:'B', name:'理科综合实验楼A', buildId:'101093843117592774',centerValue:'101093843117592774',valueList:['101093843117592774'],alarmType:false,},
- {school:'B', name:'理科综合实验楼B', buildId:'101093843117592779',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'理科综合实验楼C', buildId:'101093843117592775',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'理科综合实验楼D', buildId:'101093843117592776',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'理科综合实验楼E', buildId:'101093843117592780',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'老昆虫博物馆', buildId:'101093843117592786',centerValue:'101093843117592786',valueList:['101093843117592786'],alarmType:false,},
- {school:'B', name:'食品楼', buildId:'101093843117592789',centerValue:'101093843117592789',valueList:['101093843117592789'],alarmType:false,},
- {school:'B', name:'食品楼A', buildId:'101093843117592771',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'食品楼B', buildId:'101093843117592770',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'食品楼C', buildId:'101093843117592772',centerValue:'',valueList:[],alarmType:false,},
- {school:'B', name:'食品楼D', buildId:'101093843117592773',centerValue:'',valueList:[],alarmType:false,},
- {school:'N', name:'农科楼(农学院)', buildId:'101084081109864872',centerValue:'101084081109864872',valueList:['101084081109864872'],alarmType:false,},
- {school:'N', name:'农科楼(园艺学院)', buildId:'101084081109864874',centerValue:'',valueList:[],alarmType:false,},
- {school:'N', name:'农科楼(植物保护学院)', buildId:'101084081109864873',centerValue:'',valueList:[],alarmType:false,},
- {school:'N', name:'农科楼(资源环境学院)', buildId:'101093843117592803',centerValue:'',valueList:[],alarmType:false,},
- {school:'N', name:'农科院子校教学楼(初中)', buildId:'101093843117592823',centerValue:'',valueList:[],alarmType:false,},
- {school:'N', name:'南2号教学楼', buildId:'101093843117592817',centerValue:'101093843117592817',valueList:['101093843117592817'],alarmType:false,},
- {school:'N', name:'南3号教学楼', buildId:'101093843117592820',centerValue:'101093843117592820',valueList:['101093843117592820'],alarmType:false,},
- {school:'N', name:'南校子校楼', buildId:'101093843117592800',centerValue:'',valueList:[],alarmType:false,},
- {school:'N', name:'南校水保楼', buildId:'101093843117592801',centerValue:'',valueList:[],alarmType:false,},
- {school:'N', name:'家畜生物学重点实验室楼', buildId:'101093843117592787',centerValue:'101093843117592787',valueList:['101093843117592787'],alarmType:false,},
- {school:'N', name:'木艺坊', buildId:'101093843117592819',centerValue:'101093843117592819',valueList:['101093843117592819'],alarmType:false,},
- {school:'N', name:'林学院实验楼', buildId:'101093843117592818',centerValue:'',valueList:[],alarmType:false,},
- {school:'N', name:'科研主楼', buildId:'101093843117592784',centerValue:'101093843117592784',valueList:['101093843117592784'],alarmType:false,},
- {school:'N', name:'科研楼', buildId:'101093843117592822',centerValue:'',valueList:[],alarmType:false,},
- {school:'N', name:'经管园林楼(文科楼)A', buildId:'101093843117592777',centerValue:'101093843117592777',valueList:['101093843117592777'],alarmType:false,},
- {school:'N', name:'经管园林楼(文科楼)C', buildId:'101093843117592778',centerValue:'',valueList:[],alarmType:false,},
- {school:'N', name:'草业与草原楼', buildId:'101093843117592825',centerValue:'101093843117592825',valueList:['101093843117592825'],alarmType:false,},
- {school:'N', name:'林学院实验楼', buildId:'101093843117592821',centerValue:'101093843117592821',valueList:['101093843117592821'],alarmType:false,},
- {school:'旱研院校区', name:'农一站科研楼', buildId:'101093843117592827',centerValue:'',valueList:[],alarmType:false,},
- {school:'旱研院校区', name:'农机一站平房', buildId:'101093843117592828',centerValue:'',valueList:[],alarmType:false,},
- {school:'校外', name:'农三站科研办公楼', buildId:'101093843117592790',centerValue:'',valueList:[],alarmType:false,},
- {school:'校外', name:'器材楼', buildId:'101093843117592792',centerValue:'',valueList:[],alarmType:false,},
- {school:'水保所校区', name:'人工降雨实验大厅', buildId:'101093843117592793',centerValue:'',valueList:[],alarmType:false,},
- {school:'水保所校区', name:'南校区农科楼', buildId:'101093843117592799',centerValue:'',valueList:[],alarmType:false,},
- {school:'水保所校区', name:'国家重点实验室西楼', buildId:'101093843117592798',centerValue:'',valueList:[],alarmType:false,},
- {school:'水保所校区', name:'水保所人工干旱环境气候室工程', buildId:'101093843117592795',centerValue:'',valueList:[],alarmType:false,},
- {school:'水保所校区', name:'水保所国家重点实验室楼', buildId:'101093843117592794',centerValue:'',valueList:[],alarmType:false,},
- {school:'水保所校区', name:'水保所科研大楼', buildId:'101093843117592797',centerValue:'',valueList:[],alarmType:false,},
- {school:'水保所校区', name:'水保所西区科研楼', buildId:'101093843117592796',centerValue:'',valueList:[],alarmType:false,},
- {school:'老附中校区', name:'附中实验楼', buildId:'101093843117592830',centerValue:'',valueList:[],alarmType:false,},
- {school:'老附中校区', name:'附中教学楼', buildId:'101093843117592791',centerValue:'',valueList:[],alarmType:false,},
- ],
- // 本地
- // modelsUrlN:'/models/NxiaoQu.glb',
- // modelsUrlB:'/models/BxiaoQu.glb',
- // alarmUrl:'/png/alarm.png',
- // noAlarmUrl:'/png/noAlarm.png',
- // 本地部署
- // modelsUrlN:this.judgmentNetworkReturnAddress()?'/v3/largeScreen/models/NxiaoQu.glb':'/labAppTest/largeScreen/models/NxiaoQu.glb',
- // modelsUrlB:this.judgmentNetworkReturnAddress()?'/v3/largeScreen/models/BxiaoQu.glb':'/labAppTest/largeScreen/models/BxiaoQu.glb',
- // alarmUrl:this.judgmentNetworkReturnAddress()?'/v3/largeScreen/png/alarm.png':'/labAppTest/largeScreen/png/alarm.png',
- // noAlarmUrl:this.judgmentNetworkReturnAddress()?'/v3/largeScreen/png/noAlarm.png':'/labAppTest/largeScreen/png/noAlarm.png',
- // 线上部署
- modelsUrlN:'/largeScreen/models/NxiaoQu.glb',
- modelsUrlB:'/largeScreen/models/BxiaoQu.glb',
- alarmUrl:'/largeScreen/png/alarm.png',
- noAlarmUrl:'/largeScreen/png/noAlarm.png',
- //当前校区状态
- schoolType:'',
- };
- },
- mounted() {
- this.initThree();
- this.loadModel('N');
- this.loadAssets();
- window.addEventListener('resize', this.onWindowResize);
- },
- beforeDestroy() {
- // 停止动画循环
- if (this.animationId) {
- cancelAnimationFrame(this.animationId);
- }
- // 销毁Three.js相关资源
- this.disposeThreeResources();
- // 停止所有补间动画
- TWEEN.removeAll();
- // 移除事件监听器
- window.removeEventListener('resize', this.onWindowResize);
- this.renderer.dispose();
- },
- methods: {
- //初始化
- initThree() {
- // 初始化场景
- this.scene = new THREE.Scene();
- //场景背景色
- this.scene.background = new THREE.Color(0x020716);
- // 初始化渲染器
- this.renderer = new THREE.WebGLRenderer({ antialias: true });
- // 主渲染器尺寸设置
- this.renderer = new THREE.WebGLRenderer({ antialias: true });
- this.renderer.setSize(5927, 2160); // 固定尺寸
- this.$refs.container.appendChild(this.renderer.domElement);
- // 初始化相机时使用存储的位置
- this.camera = new THREE.PerspectiveCamera(
- 75,
- // window.innerWidth / window.innerHeight,
- 5927 / 2160,
- 0.1,
- 1000
- );
- this.camera.position.copy(this.initialCameraPosition);
- // 初始化控制器时存储初始目标
- this.controls = new OrbitControls(this.camera, this.renderer.domElement);
- this.controls.target.copy(this.initialControlsTarget);
- this.labelRenderer = new CSS2DRenderer();
- this.labelRenderer.setSize(5927, 2160);
- this.labelRenderer.domElement.style.position = 'absolute';
- this.labelRenderer.domElement.style.top = '0';
- this.labelRenderer.domElement.style.pointerEvents = 'none';//注释后 锁死鼠标视角
- this.$refs.container.appendChild(this.labelRenderer.domElement);
- // 初始化控制器
- this.controls = new OrbitControls(this.camera, this.renderer.domElement);
- this.controls.enableDamping = true;
- this.controls.dampingFactor = 0.05;
- // 添加灯光
- const ambientLight = new THREE.AmbientLight(0xffffff, 1);
- this.scene.add(ambientLight);
- const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
- directionalLight.position.set(-1.5, 2, -1.5);
- this.scene.add(directionalLight);
- // 启动动画循环
- this.animate();
- },
- //模型加载
- loadModel(type) {
- let self = this;
- this.$set(this,'schoolType',type);
- if(type == 'N'){
- //南校区
- const loaderN = new GLTFLoader();
- loaderN.load(self.modelsUrlN, (gltf) => {
- const model = gltf.scene;
- this.model = gltf.scene;
- this.scene.add(model);
- // 递归查找所有Group
- const traverseGroups = (obj) => {
- if (obj.isGroup) {
- this.registerHouse(obj);
- }
- obj.children.forEach(child => traverseGroups(child));
- };
- traverseGroups(gltf.scene);
- // 获取所有子模型并计算中心点
- model.traverse((child) => {
- if (child.isGroup) {
- const center = this.getBoundingBoxCenter(child);
- this.subModels.push({
- name: child.name,
- center: center,
- originalPosition: child.position.clone(),
- mesh: child // 直接引用mesh对象 用于材质修改
- });
- }
- });
- });
- }else if(type == 'B'){
- //北校区
- const loaderB = new GLTFLoader();
- loaderB.load(self.modelsUrlB, (gltf) => {
- const model = gltf.scene;
- this.model = gltf.scene;
- this.scene.add(model);
- // 递归查找所有Group
- const traverseGroups = (obj) => {
- if (obj.isGroup) {
- this.registerHouse(obj);
- }
- obj.children.forEach(child => traverseGroups(child));
- };
- traverseGroups(gltf.scene);
- // 获取所有子模型并计算中心点
- model.traverse((child) => {
- if (child.isGroup) {
- const center = this.getBoundingBoxCenter(child);
- this.subModels.push({
- name: child.name,
- center: center,
- originalPosition: child.position.clone(),
- mesh: child // 直接引用mesh对象 用于材质修改
- });
- }
- });
- });
- }
- setTimeout(function(){
- self.buildingApplyTexture([]);
- // self.buildingColor([]);
- },500);
- },
- /****** 新模型相关方法-开始 *******/
- // 修改后的 registerHouse 方法
- registerHouse(group) {
- const bbox = new THREE.Box3().setFromObject(group);
- const center = bbox.getCenter(new THREE.Vector3());
- // 记录原始材质
- const originalMaterials = [];
- group.traverse(child => {
- if (child.isMesh) {
- originalMaterials.push({
- mesh: child,
- material: child.material.clone() // 重要:必须克隆原始材质
- });
- }
- });
- this.houses.push({
- name:group.name,
- uuid: group.uuid,
- group: group,
- screenPosition: new THREE.Vector2(),
- worldPosition: center,
- originalMaterials: originalMaterials // 新增原始材质存储
- });
- },
- // 预加载材质和贴图
- loadAssets() {
- const textureLoader = new THREE.TextureLoader();
- this.textures = [
- textureLoader.load(this.alarmUrl)
- ];
- },
- // 报警样式
- changeTexture(house) {
- const texture = this.textures[Math.floor(Math.random() * this.textures.length)];
- texture.needsUpdate = true;
- house.group.traverse(child => {
- if (child.isMesh) {
- child.material.map = texture;
- child.material.needsUpdate = true;
- }
- });
- },
- // 恢复正常显示
- restoreOriginalMaterial(house) {
- house.originalMaterials.forEach(entry => {
- entry.mesh.material.dispose(); // 清理当前材质
- entry.mesh.material = entry.material.clone();
- entry.mesh.material.needsUpdate = true;
- });
- },
- /****** 新模型相关方法-结束 *******/
- getBoundingBoxCenter(obj) {
- const box = new THREE.Box3().setFromObject(obj);
- return box.getCenter(new THREE.Vector3());
- },
- onWindowResize() {
- this.camera.aspect = 5927 / 2160;
- this.camera.updateProjectionMatrix();
- this.renderer.setSize(5927, 2160);
- this.labelRenderer.setSize(5927, 2160);
- },
- //动画刷新
- animate() {
- if (this.model&&this.rotateType) {
- this.model.rotation.y += 0.001; // 添加简单旋转动画
- }else if(this.model&&!this.rotateType){
- this.model.rotation.y = 0;
- }
- // requestAnimationFrame(this.animate);
- // TWEEN.update();
- // this.controls.update();
- // this.renderer.render(this.scene, this.camera);
- // this.labelRenderer.render(this.scene, this.camera);
- this.animationId = requestAnimationFrame(this.animate);
- TWEEN.update();
- this.controls.update();
- this.renderer.render(this.scene, this.camera);
- this.labelRenderer.render(this.scene, this.camera);
- },
- //预案关闭
- alarmOff(){
- let self = this;
- self.$set(this,'rotateType',true)
- if(this.schoolType == 'N'){
- self.resetCamera();
- self.del2dText();
- // self.buildingColor([]);
- self.buildingApplyTexture([]);
- }else{
- //删除所有文字
- for(let i=0;i<self.scene.children.length;i++){
- if(self.scene.children[i].isCSS2DObject){
- self.scene.remove(self.scene.children[i]);
- i--
- }
- }
- //删除模型数据
- this.$set(this,'subModels',[]);
- this.$set(this,'houses',[]);
- this.scene.children.forEach((item)=>{
- if(item.type == 'Group'){
- this.scene.remove(item);
- }
- })
- self.resetCamera();
- this.delLoadModel('N');
- }
- },
- // 新增恢复视角方法
- resetCamera() {
- this.controls.enabled = false;
- new TWEEN.Tween(this.camera.position)
- .to(this.initialCameraPosition, 1500)
- .easing(TWEEN.Easing.Quadratic.InOut)
- .start();
- new TWEEN.Tween(this.controls.target)
- .to(this.initialControlsTarget, 1500)
- .easing(TWEEN.Easing.Quadratic.InOut)
- .onComplete(() => {
- this.controls.enabled = true;
- })
- .start();
- this.del2dText();
- },
- //删除所有2d文字
- del2dText(){
- let self = this;
- for(let i=0;i<self.scene.children.length;i++){
- if(self.scene.children[i].isCSS2DObject){
- this.scene.remove(self.scene.children[i]);
- i--
- }
- }
- },
- //预案触发
- alarmTrigger(list,obj,type){
- let self = this;
- self.$set(this,'rotateType',false)
- //list=所有报警数据 obj=需要聚焦的报警数据 type=报警的校区
- if(this.schoolType == type){
- //当前校区
- self.buildingApplyTexture(list);
- // self.buildingColor(list);
- self.focusingLens(obj);
- //删除所有文字
- for(let i=0;i<self.scene.children.length;i++){
- if(self.scene.children[i].isCSS2DObject){
- self.scene.remove(self.scene.children[i]);
- i--
- }
- }
- //生成新文字
- this.alarmTextLabel(list);
- }else{
- //其他校区
- //删除所有文字
- for(let i=0;i<self.scene.children.length;i++){
- if(self.scene.children[i].isCSS2DObject){
- self.scene.remove(self.scene.children[i]);
- i--
- }
- }
- //删除模型数据
- this.$set(this,'subModels',[]);
- this.$set(this,'houses',[]);
- this.scene.children.forEach((item)=>{
- if(item.type == 'Group'){
- self.scene.remove(item);
- }
- })
- //加载新模型
- this.newLoadModel(list,obj,type);
- }
- },
- //新模型加载
- newLoadModel(list,obj,type) {
- let self = this;
- this.$set(this,'schoolType',type);
- if(type == 'N'){
- //南校区
- const loaderN = new GLTFLoader();
- loaderN.load(self.modelsUrlN, (gltf) => {
- const model = gltf.scene;
- this.model = gltf.scene;
- this.scene.add(model);
- // 递归查找所有Group
- const traverseGroups = (obj) => {
- if (obj.isGroup) {
- this.registerHouse(obj);
- }
- obj.children.forEach(child => traverseGroups(child));
- };
- traverseGroups(gltf.scene);
- // 获取所有子模型并计算中心点
- model.traverse((child) => {
- if (child.isGroup) {
- const center = this.getBoundingBoxCenter(child);
- this.subModels.push({
- name: child.name,
- center: center,
- originalPosition: child.position.clone(),
- mesh: child // 直接引用mesh对象 用于材质修改
- });
- }
- });
- });
- }else if(type == 'B'){
- //北校区
- const loaderB = new GLTFLoader();
- loaderB.load(self.modelsUrlB, (gltf) => {
- const model = gltf.scene;
- this.model = gltf.scene;
- this.scene.add(model);
- // 递归查找所有Group
- const traverseGroups = (obj) => {
- if (obj.isGroup) {
- this.registerHouse(obj);
- }
- obj.children.forEach(child => traverseGroups(child));
- };
- traverseGroups(gltf.scene);
- // 获取所有子模型并计算中心点
- model.traverse((child) => {
- if (child.isGroup) {
- const center = this.getBoundingBoxCenter(child);
- this.subModels.push({
- name: child.name,
- center: center,
- originalPosition: child.position.clone(),
- mesh: child // 直接引用mesh对象 用于材质修改
- });
- }
- });
- });
- }
- setTimeout(function(){
- self.focusingLens(obj);
- self.buildingApplyTexture(list);
- // self.buildingColor(list);
- self.alarmTextLabel(list);
- },1000);
- },
- //预案停止模型加载
- delLoadModel(type) {
- let self = this;
- this.$set(this,'schoolType',type);
- //南校区
- const loaderN = new GLTFLoader();
- loaderN.load(self.modelsUrlN, (gltf) => {
- const model = gltf.scene;
- this.model = gltf.scene;
- this.scene.add(model);
- // 递归查找所有Group
- const traverseGroups = (obj) => {
- if (obj.isGroup) {
- this.registerHouse(obj);
- }
- obj.children.forEach(child => traverseGroups(child));
- };
- traverseGroups(gltf.scene);
- // 获取所有子模型并计算中心点
- model.traverse((child) => {
- if (child.isGroup) {
- const center = this.getBoundingBoxCenter(child);
- this.subModels.push({
- name: child.name,
- center: center,
- originalPosition: child.position.clone(),
- mesh: child // 直接引用mesh对象 用于材质修改
- });
- }
- });
- });
- setTimeout(function(){
- self.buildingApplyTexture([]);
- // self.buildingColor([]);
- },1000);
- },
- //初始化楼栋标记(报警时调用)
- buildingColor(list){
- let self = this;
- //处理基础数据
- self.modelBuildingList.forEach((maxItem)=>{
- let num = 0;
- list.forEach((item)=>{
- if(maxItem.buildId == item.buildId){
- num++
- }
- })
- maxItem.alarmType = num != 0;
- })
- //处理预警与正常贴图
- self.modelBuildingList.forEach((maxItem)=>{
- maxItem.valueList.forEach((bigItem)=>{
- this.scene.children.forEach((item)=>{
- if(item.type == 'Group'){
- item.children.forEach((minItem)=>{
- if(minItem.name == bigItem){
- if(maxItem.alarmType){
- self.applyTexture(self.alarmUrl,minItem);
- }else{
- self.applyTexture(self.noAlarmUrl,minItem);
- }
- }
- })
- }
- })
- })
- })
- },
- buildingApplyTexture(list){
- let self = this;
- //处理基础数据
- self.modelBuildingList.forEach((maxItem)=>{
- let num = 0;
- list.forEach((item)=>{
- if(maxItem.buildId == item.buildId){
- num++
- }
- })
- maxItem.alarmType = num != 0;
- })
- this.$forceUpdate()
- //处理预警与正常贴图
- self.modelBuildingList.forEach((maxItem)=>{
- maxItem.valueList.forEach((bigItem)=>{
- self.houses.forEach((minItem)=>{
- if(minItem.name == bigItem){
- if(maxItem.alarmType){
- self.changeTexture(minItem);
- }else{
- self.restoreOriginalMaterial(minItem);
- }
- }
- })
- })
- })
- },
- //图片材质替换
- applyTexture(textureName,obj) {
- const textureLoader = new THREE.TextureLoader();
- textureLoader.load(
- textureName, // 图片放在public目录下
- (texture) => {
- texture.flipY = false; // 根据图片格式调整
- obj.traverse((child) => {
- if (child.isMesh) {
- // 释放旧纹理资源
- if (child.material.map) {
- child.material.map.dispose();
- }
- child.material.map = texture;
- child.material.needsUpdate = true;
- }
- });
- },
- undefined,
- (error) => {
- console.error('材质加载失败:', error);
- }
- );
- },
- //材质方法调用
- changeMaterial(options) {
- const {
- modelSelector, // 支持三种选择方式:名称(name)/索引(index)/'all'
- materialParams, // 新材质参数对象
- keepOriginal = false // 是否保留原始材质
- } = options;
- const targetMeshes = this.getTargetMeshes(modelSelector);
- targetMeshes.forEach(mesh => {
- // 创建新材质
- const newMaterial = this.createMaterial(materialParams);
- // 处理旧材质
- if (!keepOriginal && mesh.userData.originalMaterial) {
- mesh.material.dispose();
- }
- // 应用新材质
- mesh.material = newMaterial;
- });
- },
- // 辅助方法:获取目标网格
- getTargetMeshes(selector) {
- if (selector === 'all') {
- return this.subModels.map(m => m.mesh);
- }
- if (typeof selector === 'number') {
- return [this.subModels[selector].mesh];
- }
- if (typeof selector === 'string') {
- return this.subModels
- .filter(m => m.name === selector)
- .map(m => m.mesh);
- }
- return [];
- },
- // 材质工厂方法
- createMaterial(params) {
- const type = params.type || 'Standard';
- const baseParams = {
- color: new THREE.Color(params.color || 0xffffff),
- map: params.texture ? this.loadTexture(params.texture) : null,
- transparent: params.transparent || false,
- opacity: params.opacity || 1.0,
- ...params
- };
- switch(type.toLowerCase()) {
- case 'basic':
- return new THREE.MeshBasicMaterial(baseParams);
- case 'phong':
- return new THREE.MeshPhongMaterial(baseParams);
- case 'standard':
- return new THREE.MeshStandardMaterial(baseParams);
- case 'physical':
- return new THREE.MeshPhysicalMaterial(baseParams);
- default:
- return new THREE.MeshStandardMaterial(baseParams);
- }
- },
- //聚焦镜头查询
- focusingLens(obj){
- let self = this;
- this.modelBuildingList.forEach((maxItem)=>{
- if(maxItem.buildId == obj.buildId){
- self.subModels.forEach((bigItem)=>{
- if(bigItem.name == maxItem.centerValue){
- self.moveCameraToModel(bigItem,bigItem.name);
- }
- })
- }
- })
- },
- //镜头聚焦方法
- moveCameraToModel(model,name) {
- let self = this;
- // 禁用控制器
- this.controls.enabled = false;
- // 计算目标位置(模型中心前上方)
- const targetPosition = new THREE.Vector3()
- .copy(model.center)
- .add(new THREE.Vector3(0.2,0.2, 0.2));
- // 动画参数
- const duration = 1500;
- const easing = TWEEN.Easing.Quadratic.InOut;
- // 相机位置动画
- new TWEEN.Tween(this.camera.position)
- .to(targetPosition, duration)
- .easing(easing)
- .start();
- // 控制器目标点动画
- new TWEEN.Tween(this.controls.target)
- .to(model.center, duration)
- .easing(easing)
- .onComplete(() => {
- this.controls.enabled = true; // 恢复控制
- })
- .start();
- },
- //预警文本生成
- alarmTextLabel(list){
- let self = this;
- let newList = [];
- self.modelBuildingList.forEach((maxItem)=>{
- list.forEach((item)=>{
- if(maxItem.buildId == item.buildId){
- let num = 0;
- newList.forEach((minItem)=>{
- if(minItem.buildId == item.buildId){
- num++
- if(!minItem.textList[1]){
- minItem.textList.push(
- "<div style='border-top:1px dashed #E90104;margin-top:35px;'>" +
- "<p style='color:#FF8400;font-size:34px;line-height:49px;margin-top:29px;'>"+item.riskPlanName+"</p>"+
- "<p style='font-size:34px;line-height:49px;margin-top:15px;'>"+this.parseTime(item.eventStartTime,"{y}-{m}-{d} {h}:{i}:{s}")+"</p>"+
- "<p style='font-size:34px;line-height:49px;margin-top:15px;'>"+item.floorName+"-"+item.roomNum+"-"+item.deptName+"</p>"+
- "<p style='font-size:34px;line-height:49px;margin-top:15px;'><span style='border:1px solid "+item.classLevelColor+";padding:0 10px;margin-right:20px;border-radius:20px;color:"+item.classLevelColor+";'>"+item.classLevelName+"</span>"+item.subName+"</p>"+
- "</div>"
- )
- }else{
- minItem.textList.splice(0,1)
- minItem.textList.push(
- "<div style='border-top:1px dashed #E90104;margin-top:35px;'>" +
- "<p style='color:#FF8400;font-size:34px;line-height:49px;margin-top:29px;'>"+item.riskPlanName+"</p>"+
- "<p style='font-size:34px;line-height:49px;margin-top:15px;'>"+this.parseTime(item.eventStartTime,"{y}-{m}-{d} {h}:{i}:{s}")+"</p>"+
- "<p style='font-size:34px;line-height:49px;margin-top:15px;'>"+item.floorName+"-"+item.roomNum+"-"+item.deptName+"</p>"+
- "<p style='font-size:34px;line-height:49px;margin-top:15px;'><span style='border:1px solid "+item.classLevelColor+";padding:0 10px;margin-right:20px;border-radius:20px;color:"+item.classLevelColor+";'>"+item.classLevelName+"</span>"+item.subName+"</p>"+
- "</div>"
- )
- }
- }
- })
- if(num == 0){
- newList.push(
- {
- buildId:item.buildId,
- centerValue:maxItem.centerValue,
- text:"",
- //校区-楼栋
- titleName:item.schoolName+' - '+item.buildName,
- textList:[
- "<div>" +
- "<p style='color:#FF8400;font-size:34px;line-height:49px;margin-top:29px;'>"+item.riskPlanName+"</p>"+
- "<p style='font-size:34px;line-height:49px;margin-top:15px;'>"+this.parseTime(item.eventStartTime,"{y}-{m}-{d} {h}:{i}:{s}")+"</p>"+
- "<p style='font-size:34px;line-height:49px;margin-top:15px;'>"+item.floorName+"-"+item.roomNum+"-"+item.deptName+"</p>"+
- "<p style='font-size:34px;line-height:49px;margin-top:15px;'><span style='border:1px solid "+item.classLevelColor+";padding:0 10px;margin-right:20px;border-radius:20px;color:"+item.classLevelColor+";'>"+item.classLevelName+"</span>"+item.subName+"</p>"+
- "</div>"
- ]
- }
- )
- }
- }
- })
- })
- //循环定位模型位置并生成textTable
- newList.forEach((maxItem,maxIndex)=>{
- maxItem.text = "<div style='z-index:"+maxIndex+";border-radius:20px;padding:30px;color:#ffffff;border:1px solid #E90104;box-shadow: inset 0 0 10px rgba(176, 0, 0, 1);background-color: rgba(187,0,0,0.5)'>"
- maxItem.text = maxItem.text+"<p style='border-bottom:1px solid #E90104;line-height:104px;'>"+maxItem.titleName+"</p>";
- maxItem.textList.forEach((html)=>{
- maxItem.text = maxItem.text + html
- })
- maxItem.text = maxItem.text+"</div>"
- self.subModels.forEach((bigItem)=>{
- if(bigItem.name == maxItem.centerValue){
- forScene(bigItem,maxItem.text);
- }
- })
- })
- function forScene(model,text){
- self.scene.children.forEach((item)=>{
- if(item.type == 'Group'){
- item.children.forEach((minItem)=>{
- if(minItem.name == model.name){
- self.add2dFont(minItem,model.center,text);
- }
- })
- }
- })
- }
- },
- //生成css2d文字
- add2dFont(model,center,text){
- const labelDiv = document.createElement('div');
- labelDiv.style.fontSize = '40px';
- labelDiv.style.width = '800px';
- labelDiv.style.borderRadius = '10px';
- labelDiv.style.whiteSpace = "pre-line";
- labelDiv.innerHTML = text;
- // 创建 CSS2D 对象并绑定到目标
- const label = new CSS2DObject(labelDiv);
- const targetPosition = new THREE.Vector3()
- .copy(center)
- .add(new THREE.Vector3(0.09,0.08,-0.1));
- label.position.set(targetPosition.x,targetPosition.y,targetPosition.z); // 在对象上方 1 单位位置
- this.scene.add(label);
- },
- //内外网地址判定
- judgmentNetworkReturnAddress() {
- /*判断是否是内网IP*/
- // 获取当前页面url
- var curPageUrl = window.location.href;
- var reg1 = /(http|ftp|https|www):\/\//g;//去掉前缀
- curPageUrl =curPageUrl.replace(reg1,'');
- var reg2 = /\:+/g;//替换冒号为一点
- curPageUrl =curPageUrl.replace(reg2,'.');
- curPageUrl = curPageUrl.split('.');//通过一点来划分数组
- var ipAddress = curPageUrl[0]+'.'+curPageUrl[1]+'.'+curPageUrl[2]+'.'+curPageUrl[3];
- var isInnerIp = false;//默认给定IP不是内网IP
- var ipNum = getIpNum(ipAddress);
- /**
- * 私有IP:A类 10.0.0.0 -10.255.255.255
- * B类 172.16.0.0 -172.31.255.255
- * C类 192.168.0.0 -192.168.255.255
- * D类 127.0.0.0 -127.255.255.255(环回地址)
- **/
- var aBegin = getIpNum("10.0.0.0");
- var aEnd = getIpNum("10.255.255.255");
- var bBegin = getIpNum("172.16.0.0");
- var bEnd = getIpNum("172.31.255.255");
- var cBegin = getIpNum("192.168.0.0");
- var cEnd = getIpNum("192.168.255.255");
- var dBegin = getIpNum("127.0.0.0");
- var dEnd = getIpNum("127.255.255.255");
- isInnerIp = isInner(ipNum,aBegin,aEnd) || isInner(ipNum,bBegin,bEnd) || isInner(ipNum,cBegin,cEnd) || isInner(ipNum,dBegin,dEnd);
- return isInnerIp?true:false;
- /*获取IP数*/
- function getIpNum(ipAddress) {
- var ip = ipAddress.split(".");
- var a = parseInt(ip[0]);
- var b = parseInt(ip[1]);
- var c = parseInt(ip[2]);
- var d = parseInt(ip[3]);
- var ipNum = a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
- return ipNum;
- }
- function isInner(userIp,begin,end){
- return (userIp>=begin) && (userIp<=end);
- }
- },
- // 资源销毁方法
- disposeThreeResources() {
- // 释放几何体和材质
- this.scene.traverse(child => {
- if (child.isMesh) {
- child.geometry.dispose();
- if (Array.isArray(child.material)) {
- child.material.forEach(m => m.dispose());
- } else {
- child.material.dispose();
- }
- }
- });
- // 释放渲染器
- this.renderer.dispose();
- this.labelRenderer.domElement.remove();
- this.labelRenderer = null;
- // 释放场景和相机
- this.scene = null;
- this.camera = null;
- // 释放控制器
- this.controls.dispose();
- this.controls = null;
- },
- }
- };
- </script>
- <style scoped lang="scss">
- .canvasMap{
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- }
- </style>
|