ThreeModel1.vue 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344
  1. <template>
  2. <div class="three-model">
  3. <loading ref="pageLoading"></loading>
  4. <div class="map-3d" :style="'transform: rotate(' + mapDeg + 'deg);'">
  5. <img :src="require('@assets/png/3dmap.png')" alt="" />
  6. </div>
  7. <div class="three-html-layer">
  8. <div
  9. v-for="(info, index) of htmlLayer"
  10. :key="index"
  11. class="three-html-dom fan-info"
  12. :id="info.id"
  13. v-show="info.show"
  14. :style="'left: ' + info.x + 'px; top: ' + info.y + 'px;'"
  15. >
  16. <div
  17. class="fan-name pointer"
  18. v-show="info.id != 'build'"
  19. @click.stop="info.clickName"
  20. >
  21. {{ info.name }}
  22. </div>
  23. <div
  24. class="fan-can-click"
  25. @click.stop="info.clickFan"
  26. :style="
  27. 'left: ' +
  28. info.fanX +
  29. 'px; top: ' +
  30. info.fanY +
  31. 'px; width: ' +
  32. info.fanW +
  33. 'px; height: ' +
  34. info.fanH +
  35. 'px;'
  36. "
  37. ></div>
  38. </div>
  39. <div
  40. class="three-html-dom build-info"
  41. :id="fanInfoLayer.id"
  42. v-show="fanInfoLayer.show"
  43. :style="
  44. 'left: ' + fanInfoLayer.x + 'px; top: ' + fanInfoLayer.y + 'px;'
  45. "
  46. >
  47. <div class="build-info-close" @click.stop="hideFanInfo">
  48. <i class="el-icon-close"></i>
  49. </div>
  50. <div
  51. class="build-info-item purple"
  52. :style="
  53. 'left: ' + circleXY[0].x + 'px; top: -' + circleXY[0].y + 'px'
  54. "
  55. >
  56. <div class="build-info-item-num">{{ jkd }}</div>
  57. <div class="build-info-item-text">健康度</div>
  58. </div>
  59. <div
  60. class="build-info-item blue"
  61. :style="
  62. 'left: ' + circleXY[1].x + 'px; top: -' + circleXY[1].y + 'px'
  63. "
  64. >
  65. <div class="build-info-item-num">{{ ysl }}</div>
  66. <div class="build-info-item-text">优</div>
  67. </div>
  68. <div
  69. class="build-info-item yellow"
  70. :style="
  71. 'left: ' + circleXY[2].x + 'px; top: -' + circleXY[2].y + 'px'
  72. "
  73. >
  74. <div class="build-info-item-num">{{ lsl }}</div>
  75. <div class="build-info-item-text">良</div>
  76. </div>
  77. <div
  78. class="build-info-item orange"
  79. :style="
  80. 'left: ' + circleXY[3].x + 'px; top: -' + circleXY[3].y + 'px'
  81. "
  82. >
  83. <div class="build-info-item-num">{{ csl }}</div>
  84. <div class="build-info-item-text">差</div>
  85. </div>
  86. <div
  87. class="build-info-item red"
  88. :style="
  89. 'left: ' + circleXY[4].x + 'px; top: -' + circleXY[4].y + 'px'
  90. "
  91. >
  92. <div class="build-info-item-num">{{ gzsl }}</div>
  93. <div class="build-info-item-text">故障</div>
  94. </div>
  95. </div>
  96. </div>
  97. </div>
  98. </template>
  99. <script>
  100. import loading from "@com/coms/loading/loading.vue";
  101. import * as THREE from "three";
  102. import { GLTFLoader } from "@node/three/examples/jsm/loaders/GLTFLoader.js";
  103. import { OrbitControls } from "@node/three/examples/jsm/controls/OrbitControls.js";
  104. import { GeometryUtils } from "@node/three/examples/jsm/utils/GeometryUtils.js";
  105. let camera, scene, renderer, controls;
  106. let mixers = [];
  107. let clock = new THREE.Clock();
  108. let fanAnimates = [];
  109. let fans = [];
  110. // let cylinder = null;
  111. export default {
  112. // 名称
  113. name: "ThreeModel1",
  114. // 使用组件
  115. components: {
  116. loading,
  117. },
  118. // 传入参数
  119. props: {
  120. data: {
  121. type: Object,
  122. default: () => {},
  123. },
  124. },
  125. // 自定义事件
  126. emits: {
  127. when: null,
  128. },
  129. // 数据
  130. data() {
  131. const that = this;
  132. return {
  133. mapSource: {},
  134. pointer: false,
  135. jkd: null,
  136. ysl: null,
  137. lsl: null,
  138. csl: null,
  139. gzsl: null,
  140. htmlLayer: [
  141. // 麻黄山
  142. {
  143. id: "fan-mhs",
  144. x: 0,
  145. y: 0,
  146. show: true,
  147. ox: 50,
  148. oy: 10,
  149. fanW: 60,
  150. fanH: 100,
  151. fanX: 0,
  152. fanY: -110,
  153. position: null,
  154. name: "麻黄山",
  155. clickName: function () {
  156. that.clickMapItem(
  157. [
  158. "MHS_FDC_SC",
  159. "MG01_01_TD",
  160. "MG01_02_TD",
  161. "MG01_03_TD",
  162. "MG01_04_TD",
  163. "MG01_05_TD",
  164. ],
  165. "MHS_FDC"
  166. );
  167. },
  168. clickFan: () => {
  169. that.showFanInfo(that.htmlLayer[0].position);
  170. that.jkd = that.htmlLayer[0].jkd;
  171. that.ysl = that.htmlLayer[0].ysl;
  172. that.lsl = that.htmlLayer[0].lsl;
  173. that.csl = that.htmlLayer[0].csl;
  174. that.gzsl = that.htmlLayer[0].gzsl;
  175. that.clickMapItem(
  176. [
  177. "MHS_FDC_SC",
  178. "MG01_01_TD",
  179. "MG01_02_TD",
  180. "MG01_03_TD",
  181. "MG01_04_TD",
  182. "MG01_05_TD",
  183. ],
  184. "MHS_FDC"
  185. );
  186. },
  187. },
  188. // 牛首山
  189. {
  190. id: "fan-nss",
  191. x: 0,
  192. y: 0,
  193. show: true,
  194. ox: 35,
  195. oy: 0,
  196. fanW: 60,
  197. fanH: 100,
  198. fanX: 0,
  199. fanY: -110,
  200. position: null,
  201. name: "牛首山",
  202. clickName: function () {
  203. that.clickMapItem(
  204. [
  205. "NSS_FDC_SC",
  206. "NG01_27_TD",
  207. "NG01_28_TD",
  208. "NG01_30_TD",
  209. "NG01_31_TD",
  210. "NG01_32_TD",
  211. ],
  212. "NSS_FDC"
  213. );
  214. },
  215. clickFan: () => {
  216. that.showFanInfo(that.htmlLayer[1].position);
  217. that.jkd = that.htmlLayer[1].jkd;
  218. that.ysl = that.htmlLayer[1].ysl;
  219. that.lsl = that.htmlLayer[1].lsl;
  220. that.csl = that.htmlLayer[1].csl;
  221. that.gzsl = that.htmlLayer[1].gzsl;
  222. that.clickMapItem(
  223. [
  224. "NSS_FDC_SC",
  225. "NG01_27_TD",
  226. "NG01_28_TD",
  227. "NG01_30_TD",
  228. "NG01_31_TD",
  229. "NG01_32_TD",
  230. ],
  231. "NSS_FDC"
  232. );
  233. },
  234. },
  235. // 青山
  236. {
  237. id: "fan-qs",
  238. x: 0,
  239. y: 0,
  240. show: true,
  241. ox: 60,
  242. oy: 10,
  243. fanW: 60,
  244. fanH: 100,
  245. fanX: 0,
  246. fanY: -100,
  247. position: null,
  248. name: "青山",
  249. clickName: function () {
  250. that.clickMapItem(
  251. [
  252. "QS_FDC_SC",
  253. "QG01_67_TD",
  254. "QG01_69_TD",
  255. "QG01_71_TD",
  256. "QG01_72_TD",
  257. "QG01_73_TD",
  258. ],
  259. "QS_FDC"
  260. );
  261. },
  262. clickFan: () => {
  263. that.showFanInfo(that.htmlLayer[2].position);
  264. that.jkd = that.htmlLayer[2].jkd;
  265. that.ysl = that.htmlLayer[2].ysl;
  266. that.lsl = that.htmlLayer[2].lsl;
  267. that.csl = that.htmlLayer[2].csl;
  268. that.gzsl = that.htmlLayer[2].gzsl;
  269. that.clickMapItem(
  270. [
  271. "QS_FDC_SC",
  272. "QG01_67_TD",
  273. "QG01_69_TD",
  274. "QG01_71_TD",
  275. "QG01_72_TD",
  276. "QG01_73_TD",
  277. ],
  278. "QS_FDC"
  279. );
  280. },
  281. },
  282. // 石板泉
  283. {
  284. id: "fan-sbq",
  285. x: 0,
  286. y: 0,
  287. show: true,
  288. ox: 40,
  289. oy: 0,
  290. fanW: 60,
  291. fanH: 90,
  292. fanX: 0,
  293. fanY: -90,
  294. position: null,
  295. name: "石板泉",
  296. clickName: function () {
  297. that.clickMapItem(
  298. [
  299. "SBQ_FDC_SC",
  300. "SG01_01_TD",
  301. "SG01_02_TD",
  302. "SG01_03_TD",
  303. "SG01_04_TD",
  304. "SG01_05_TD",
  305. ],
  306. "SBQ_FDC"
  307. );
  308. },
  309. clickFan: () => {
  310. that.showFanInfo(that.htmlLayer[3].position);
  311. that.jkd = that.htmlLayer[3].jkd;
  312. that.ysl = that.htmlLayer[3].ysl;
  313. that.lsl = that.htmlLayer[3].lsl;
  314. that.csl = that.htmlLayer[3].csl;
  315. that.gzsl = that.htmlLayer[3].gzsl;
  316. that.clickMapItem(
  317. [
  318. "SBQ_FDC_SC",
  319. "SG01_01_TD",
  320. "SG01_02_TD",
  321. "SG01_03_TD",
  322. "SG01_04_TD",
  323. "SG01_05_TD",
  324. ],
  325. "SBQ_FDC"
  326. );
  327. },
  328. },
  329. // 香山
  330. {
  331. id: "fan-xs",
  332. x: 0,
  333. y: 0,
  334. show: true,
  335. ox: 25,
  336. oy: 20,
  337. fanW: 70,
  338. fanH: 100,
  339. fanX: -15,
  340. fanY: -110,
  341. position: null,
  342. name: "香山",
  343. clickName: function () {
  344. that.clickMapItem(
  345. [
  346. "XS_FDC_SC",
  347. "XG01_26_TD",
  348. "XG01_27_TD",
  349. "XG01_29_TD",
  350. "XG01_30_TD",
  351. "XG01_31_TD",
  352. ],
  353. "XS_FDC"
  354. );
  355. },
  356. clickFan: () => {
  357. that.showFanInfo(that.htmlLayer[4].position);
  358. that.jkd = that.htmlLayer[4].jkd;
  359. that.ysl = that.htmlLayer[4].ysl;
  360. that.lsl = that.htmlLayer[4].lsl;
  361. that.csl = that.htmlLayer[4].csl;
  362. that.gzsl = that.htmlLayer[4].gzsl;
  363. that.clickMapItem(
  364. [
  365. "XS_FDC_SC",
  366. "XG01_26_TD",
  367. "XG01_27_TD",
  368. "XG01_29_TD",
  369. "XG01_30_TD",
  370. "XG01_31_TD",
  371. ],
  372. "XS_FDC"
  373. );
  374. },
  375. },
  376. // 大武口
  377. {
  378. id: "light-dwk",
  379. x: 0,
  380. y: 0,
  381. show: true,
  382. ox: 330,
  383. oy: 80,
  384. fanW: 60,
  385. fanH: 80,
  386. fanX: 0,
  387. fanY: -80,
  388. position: null,
  389. name: "大武口",
  390. clickName: function () {
  391. console.log("大武口");
  392. },
  393. clickFan: () => {
  394. console.log("大武口");
  395. },
  396. },
  397. // 平罗
  398. {
  399. id: "light-pl",
  400. x: 0,
  401. y: 0,
  402. show: true,
  403. ox: 330,
  404. oy: 80,
  405. fanW: 60,
  406. fanH: 80,
  407. fanX: 0,
  408. fanY: -80,
  409. position: null,
  410. name: "平罗",
  411. clickName: function () {
  412. console.log("平罗");
  413. },
  414. clickFan: () => {
  415. console.log("平罗");
  416. },
  417. },
  418. // 马场湖
  419. {
  420. id: "light-mch",
  421. x: 0,
  422. y: 0,
  423. show: true,
  424. ox: 230,
  425. oy: 80,
  426. fanW: 60,
  427. fanH: 80,
  428. fanX: 0,
  429. fanY: -80,
  430. position: null,
  431. name: "马场湖",
  432. clickName: function () {
  433. console.log("马场湖");
  434. },
  435. clickFan: () => {
  436. console.log("马场湖");
  437. },
  438. },
  439. // 宣和
  440. {
  441. id: "light-xh",
  442. x: 0,
  443. y: 0,
  444. show: true,
  445. ox: 260,
  446. oy: 90,
  447. fanW: 60,
  448. fanH: 80,
  449. fanX: 0,
  450. fanY: -80,
  451. position: null,
  452. name: "宣和",
  453. clickName: function () {
  454. console.log("宣和");
  455. },
  456. clickFan: () => {
  457. console.log("宣和");
  458. },
  459. },
  460. // 海子井
  461. {
  462. id: "light-hzj",
  463. x: 0,
  464. y: 0,
  465. show: true,
  466. ox: 290,
  467. oy: 120,
  468. fanW: 60,
  469. fanH: 80,
  470. fanX: 0,
  471. fanY: -80,
  472. position: null,
  473. name: "海子井",
  474. clickName: function () {
  475. console.log("海子井");
  476. },
  477. clickFan: () => {
  478. console.log("海子井");
  479. },
  480. },
  481. // 大楼
  482. {
  483. id: "build",
  484. x: 0,
  485. y: 0,
  486. show: true,
  487. ox: 290,
  488. oy: 120,
  489. fanW: 60,
  490. fanH: 100,
  491. fanX: -260,
  492. fanY: -200,
  493. position: null,
  494. name: "国电大厦",
  495. clickName: function () {
  496. that.clickMapItem(
  497. [
  498. "SBQ_FDC_SC",
  499. "NSS_FDC_SC",
  500. "QS_FDC_SC",
  501. "MHS_FDC_SC",
  502. "XS_FDC_SC",
  503. "PL_GDC_SC",
  504. ],
  505. "0"
  506. );
  507. },
  508. clickFan: () => {
  509. that.showFanInfo(that.htmlLayer[10].position);
  510. that.jkd = that.htmlLayer[10].jkd;
  511. that.ysl = that.htmlLayer[10].ysl;
  512. that.lsl = that.htmlLayer[10].lsl;
  513. that.csl = that.htmlLayer[10].csl;
  514. that.gzsl = that.htmlLayer[10].gzsl;
  515. that.clickMapItem(
  516. [
  517. "SBQ_FDC_SC",
  518. "NSS_FDC_SC",
  519. "QS_FDC_SC",
  520. "MHS_FDC_SC",
  521. "XS_FDC_SC",
  522. "PL_GDC_SC",
  523. ],
  524. "0"
  525. );
  526. },
  527. },
  528. ],
  529. fanInfoLayer: {
  530. id: "fan-info",
  531. x: 0,
  532. y: 0,
  533. show: false,
  534. ox: 70,
  535. oy: -40,
  536. position: null,
  537. },
  538. circleXY: [
  539. {
  540. x: 130 * Math.cos((180 / 180) * Math.PI) - 33,
  541. y: 130 * Math.sin((180 / 180) * Math.PI) + 33,
  542. },
  543. {
  544. x: 130 * Math.cos((135 / 180) * Math.PI) - 33,
  545. y: 130 * Math.sin((135 / 180) * Math.PI) + 33,
  546. },
  547. {
  548. x: 130 * Math.cos((90 / 180) * Math.PI) - 33,
  549. y: 130 * Math.sin((90 / 180) * Math.PI) + 33,
  550. },
  551. {
  552. x: 130 * Math.cos((45 / 180) * Math.PI) - 33,
  553. y: 130 * Math.sin((45 / 180) * Math.PI) + 33,
  554. },
  555. {
  556. x: 130 * Math.cos((0 / 180) * Math.PI) - 33,
  557. y: 130 * Math.sin((0 / 180) * Math.PI) + 33,
  558. },
  559. ],
  560. colors: [
  561. { colorName: "green", state: "dj", stateName: "待机", color: 0x05bb4c },
  562. { colorName: "blue", state: "yx", stateName: "运行", color: 0x4b55ae },
  563. { colorName: "pink", state: "xd", stateName: "限电", color: 0xc531c7 },
  564. { colorName: "red", state: "gz", stateName: "故障", color: 0xba3237 },
  565. {
  566. colorName: "orange",
  567. state: "jx",
  568. stateName: "检修",
  569. color: 0xe17e23,
  570. },
  571. { colorName: "gray", state: "lx", stateName: "离线", color: 0x606769 },
  572. { colorName: "white", state: "sl", stateName: "受累", color: 0xffffff },
  573. ],
  574. fanName: "",
  575. playAnimation: true,
  576. mapDeg: 0,
  577. };
  578. },
  579. // 函数
  580. methods: {
  581. rinseData() {
  582. if (this.mapSource && this.mapSource.MHS_FDC) {
  583. for (let pKey in this.mapSource) {
  584. if (pKey === "all") {
  585. for (let cKey in this.mapSource[pKey]) {
  586. this.htmlLayer[10][cKey] = this.mapSource[pKey][cKey];
  587. }
  588. }
  589. this.htmlLayer.forEach((ele) => {
  590. if (
  591. pKey.toLowerCase().indexOf(ele.id.toLowerCase().split("-")[1]) !==
  592. -1
  593. ) {
  594. for (let cKey in this.mapSource[pKey]) {
  595. ele[cKey] = this.mapSource[pKey][cKey];
  596. }
  597. }
  598. });
  599. }
  600. }
  601. },
  602. // 点击风场或者光伏传回点击的值
  603. clickMapItem(videoArray = [], wpId = "") {
  604. this.$emit("clickMapItem", videoArray, wpId);
  605. },
  606. // Vector3 to screen
  607. vector3ToScreen(position) {
  608. const centerX = this.$el.scrollWidth / 2;
  609. const centerY = this.$el.scrollHeight / 2;
  610. const v3 = new THREE.Vector3(position.x, position.y, position.z);
  611. const standardVec = v3.project(camera);
  612. const screenX = Math.round(centerX * standardVec.x + centerX);
  613. const screenY = Math.round(-centerY * standardVec.y + centerY);
  614. return { x: screenX, y: screenY };
  615. },
  616. // 场景
  617. initScene() {
  618. scene = new THREE.Scene();
  619. // scene.background = new THREE.Color(0xa0a0a0);
  620. },
  621. // 相机
  622. initCamera() {
  623. camera = new THREE.PerspectiveCamera(
  624. 45,
  625. this.$el.scrollWidth / this.$el.scrollHeight,
  626. 0.1,
  627. 10000
  628. );
  629. camera.position.set(50, 50, 50);
  630. },
  631. // 坐标轴
  632. initAxesHelper() {
  633. const axesHelper = new THREE.AxesHelper(150);
  634. scene.add(axesHelper);
  635. axesHelper.position.set(0, 0, 0);
  636. },
  637. // 渲染器
  638. initRender() {
  639. renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  640. // renderer.setClearAlpha(0);
  641. renderer.setSize(this.$el.scrollWidth, this.$el.scrollHeight);
  642. this.$el.append(renderer.domElement);
  643. },
  644. // 灯光
  645. initLight() {
  646. // let light = new THREE.PointLight(0xffffff, 2);
  647. let light = new THREE.AmbientLight(0xffffff, 5);
  648. // light.position.set(50, 50, 50);
  649. scene.add(light);
  650. },
  651. // 控制器
  652. initControls() {
  653. controls = new OrbitControls(camera, renderer.domElement);
  654. controls.enablePan = false;
  655. controls.maxPolarAngle = (45 / 180) * Math.PI;
  656. controls.minPolarAngle = (45 / 180) * Math.PI;
  657. controls.maxAzimuthAngle = (45 / 180) * Math.PI;
  658. controls.minAzimuthAngle = (45 / 180) * Math.PI;
  659. controls.enableKeys = false;
  660. controls.enableZoom = false;
  661. controls.update();
  662. // controls.maxAzimuthAngle = 50 / 180 * Math.PI;
  663. // controls.minAzimuthAngle = 40 / 180 * Math.PI;
  664. // controls.addEventListener("change", () => {
  665. // let cDeg = controls.getAzimuthalAngle() / Math.PI * 180;
  666. // this.mapDeg = cDeg - 40 - 5;
  667. // this.setEveryHTML();
  668. // });
  669. },
  670. // 初始化一个风机动画
  671. initFanAnimate(obj) {
  672. let fanAnimateObj = {
  673. speed: 0.05,
  674. rotate: 0,
  675. fan: obj.getObjectByName(`${obj.name}_leaf`),
  676. fan1: obj.getObjectByName(`${obj.name}_leaf_1`),
  677. fan2: obj.getObjectByName(`${obj.name}_leaf_2`),
  678. fan3: obj.getObjectByName(`${obj.name}_leaf_3`),
  679. };
  680. let fanAnimateFunction = function () {
  681. // fanAnimateObj.fan.rotateOnAxis(new THREE.Vector3(0, 1, 0), fanAnimateObj.speed);
  682. fanAnimateObj.fan.rotateY(fanAnimateObj.speed);
  683. // fanAnimateObj.fan1.rotateZ(fanAnimateObj.speed);
  684. // fanAnimateObj.fan2.rotateZ(fanAnimateObj.speed);
  685. // fanAnimateObj.fan3.rotateZ(fanAnimateObj.speed);
  686. // fanAnimateObj.rotate += fanAnimateObj.speed;
  687. // if (fanAnimateObj.rotate >= 360) {
  688. // fanAnimateObj.rotate = 0;
  689. // }
  690. fanAnimateObj.animateId =
  691. window.requestAnimationFrame(fanAnimateFunction);
  692. };
  693. fanAnimateObj.stop = function () {
  694. window.cancelAnimationFrame(fanAnimateObj.animateId);
  695. };
  696. fanAnimateObj.start = function () {
  697. fanAnimateFunction();
  698. };
  699. fanAnimateFunction();
  700. fanAnimates.push(fanAnimateObj);
  701. return fanAnimateObj;
  702. },
  703. // 清空风机动画
  704. clearFanAnimate: function () {
  705. while (fanAnimates.length > 0) {
  706. let fanAnimateObj = fanAnimates.shift();
  707. fanAnimateObj.stop();
  708. }
  709. this.playAnimation = false;
  710. },
  711. // 开始风机动画
  712. startFanAnimate: function () {
  713. fans.forEach((fan) => {
  714. this.initFanAnimate(fan);
  715. });
  716. this.playAnimation = true;
  717. },
  718. // 内容
  719. initContent() {
  720. // 加载3D地面
  721. let loaderGround = new GLTFLoader(); /*实例化加载器*/
  722. loaderGround.load(
  723. "static/3d/fanvar.gltf",
  724. (gltf) => {
  725. gltf.scene.position.set(17, 10, -12);
  726. scene.add(gltf.scene);
  727. let rootNode = gltf.scene.children[0];
  728. let fan = gltf.scene.children[0].children[0];
  729. rootNode.remove(fan);
  730. this.setFanPosition(rootNode, fan);
  731. },
  732. (xhr) => {},
  733. function (error) {
  734. console.error("load error!" + error.getWebGLErrorMessage());
  735. }
  736. );
  737. loaderGround.load(
  738. "static/3d/light.gltf",
  739. (gltf) => {
  740. gltf.scene.position.set(17, 10, -12);
  741. scene.add(gltf.scene);
  742. let rootNode = gltf.scene.children[0];
  743. let light = gltf.scene.children[0].children[0];
  744. rootNode.remove(light);
  745. this.setLightPosition(rootNode, light);
  746. },
  747. (xhr) => {},
  748. function (error) {
  749. console.error("load error!" + error.getWebGLErrorMessage());
  750. }
  751. );
  752. loaderGround.load(
  753. "static/3d/build.gltf",
  754. (gltf) => {
  755. gltf.scene.position.set(0, 0, -35); // 10, 0, -50 -6, 0.13, -50
  756. scene.add(gltf.scene);
  757. gltf.scene.rotateY(120);
  758. this.htmlLayer[10].position = {
  759. x: -6,
  760. y: -4,
  761. z: -35,
  762. };
  763. scene.onAfterRender = () => {
  764. this.$emit("when");
  765. this.$refs.pageLoading.hide();
  766. this.setEveryHTML();
  767. };
  768. },
  769. (xhr) => {},
  770. function (error) {
  771. console.error("load error!" + error.getWebGLErrorMessage());
  772. }
  773. );
  774. },
  775. // 改变元素底盘的颜色(带底盘的)
  776. changeObjectColor(obj, colorName) {
  777. for (let i = 1; i <= 4; i++) {
  778. let c = obj.getObjectByName(`${obj.name}_c_${i}`);
  779. c.material = new THREE.MeshBasicMaterial({
  780. color: 0xf0f0f0,
  781. opacity: 0.8,
  782. transparent: true,
  783. });
  784. }
  785. let leaf = obj.getObjectByName(`${obj.name}_leaf`);
  786. for (let chind of leaf.children) {
  787. chind.material = new THREE.MeshBasicMaterial({
  788. color: 0xf0f0f0,
  789. opacity: 0.8,
  790. transparent: true,
  791. });
  792. }
  793. let chassis = obj.getObjectByName(`${obj.name}_chassis`);
  794. // let color = this.colors.find(t => t.colorName == colorName || t.state == colorName || t.stateName == colorName).color;
  795. chassis.material = new THREE.MeshBasicMaterial({
  796. // color: color,
  797. color: 0xf0f0f0,
  798. opacity: 0.8,
  799. transparent: true,
  800. });
  801. },
  802. // 改变元素的颜色(光伏)
  803. changeElColor(obj, colorName) {
  804. // let color = this.colors.find(t => t.colorName == colorName || t.state == colorName || t.stateName == colorName).color;
  805. // for (let i = 1; i <= 6; i++) {
  806. // let el = obj.getObjectByName(`${obj.name}_item_${i}`);
  807. // el.material = new THREE.MeshBasicMaterial({
  808. // color: color,
  809. // opacity: 0.8,
  810. // transparent: true,
  811. // });
  812. // }
  813. },
  814. // 设置风机name
  815. setFanName(obj, name) {
  816. obj.name = name;
  817. obj.userData.name = name;
  818. // "Cylinder881" "Cone277"
  819. let Cylinder881 = obj.getObjectByName("Cylinder881");
  820. let Cylinder882 = obj.getObjectByName("Cylinder882");
  821. let Cylinder883 = obj.getObjectByName("Cylinder883");
  822. let Cone277 = obj.getObjectByName("Cone277");
  823. Cylinder881.name = `${name}_c_1`;
  824. Cylinder881.userData.name = `${name}_c_1`;
  825. Cylinder882.name = `${name}_c_2`;
  826. Cylinder882.userData.name = `${name}_c_2`;
  827. Cylinder883.name = `${name}_c_3`;
  828. Cylinder883.userData.name = `${name}_c_3`;
  829. Cone277.name = `${name}_c_4`;
  830. Cone277.userData.name = `${name}_c_4`;
  831. // "Box719")
  832. let fanBig1_1 = obj.getObjectByName("Box719");
  833. let fanBig1_2 = obj.getObjectByName("Box720");
  834. let fanBig1_3 = obj.getObjectByName("Box721");
  835. fanBig1_1.name = `${name}_leaf_1`;
  836. fanBig1_1.userData.name = `${name}_leaf_1`;
  837. fanBig1_2.name = `${name}_leaf_2`;
  838. fanBig1_2.userData.name = `${name}_leaf_2`;
  839. fanBig1_3.name = `${name}_leaf_3`;
  840. fanBig1_3.userData.name = `${name}_leaf_3`;
  841. obj.children[obj.children.length - 1].name = `${name}_chassis`;
  842. obj.children[obj.children.length - 1].userData.name = `${name}_chassis`;
  843. obj.remove(fanBig1_1, fanBig1_2, fanBig1_3);
  844. var group = new THREE.Group();
  845. // var group = new THREE.Mesh(new THREE.BoxGeometry(0.01, 0.01, 0.01),
  846. // new THREE.MeshBasicMaterial({
  847. // color: 0xff0000
  848. // })
  849. // );
  850. group.position.set(
  851. group.position.x + 0.0067,
  852. group.position.y,
  853. group.position.z + 0.01
  854. );
  855. fanBig1_1.position.set(
  856. fanBig1_1.position.x - 0.0067,
  857. fanBig1_1.position.y,
  858. fanBig1_1.position.z - 0.01
  859. );
  860. fanBig1_2.position.set(
  861. fanBig1_2.position.x - 0.0067,
  862. fanBig1_2.position.y,
  863. fanBig1_2.position.z - 0.01
  864. );
  865. fanBig1_3.position.set(
  866. fanBig1_3.position.x - 0.0067,
  867. fanBig1_3.position.y,
  868. fanBig1_3.position.z - 0.01
  869. );
  870. group.add(fanBig1_1, fanBig1_2, fanBig1_3);
  871. obj.add(group);
  872. group.name = `${name}_leaf`;
  873. },
  874. // 设置风机位置 颜色
  875. setFanPosition(rootNode, obj) {
  876. // 麻黄山
  877. let fan_mhs = obj.clone(true);
  878. fan_mhs.position.set(10, 5, -4);
  879. this.setFanName(fan_mhs, "fan_mhs");
  880. rootNode.add(fan_mhs);
  881. this.htmlLayer[0].position = fan_mhs.position;
  882. fan_mhs.score = this.htmlLayer[0].jkd;
  883. this.initCylinderGeometry(fan_mhs);
  884. // 牛首山
  885. let fan_nss = obj.clone(true);
  886. fan_nss.position.set(-4, 4, -15);
  887. this.setFanName(fan_nss, "fan_nss");
  888. rootNode.add(fan_nss);
  889. this.htmlLayer[1].position = fan_nss.position;
  890. fan_nss.score = this.htmlLayer[1].jkd;
  891. this.initCylinderGeometry(fan_nss); // 故障 显示红色圆柱
  892. // 青山
  893. let fan_qs = obj.clone(true);
  894. fan_qs.position.set(15, 5, -9);
  895. this.setFanName(fan_qs, "fan_qs");
  896. rootNode.add(fan_qs);
  897. this.htmlLayer[2].position = fan_qs.position;
  898. fan_qs.score = this.htmlLayer[2].jkd;
  899. this.initCylinderGeometry(fan_qs);
  900. // 石板泉
  901. let fan_sbq = obj.clone(true);
  902. fan_sbq.position.set(4, 5, -2);
  903. this.setFanName(fan_sbq, "fan_sbq");
  904. rootNode.add(fan_sbq);
  905. this.htmlLayer[3].position = fan_sbq.position;
  906. fan_sbq.score = this.htmlLayer[3].jkd;
  907. this.initCylinderGeometry(fan_sbq);
  908. // 香山
  909. let fan_xs = obj.clone(true);
  910. fan_xs.position.set(-9, 7, 21);
  911. this.setFanName(fan_xs, "fan_xs");
  912. rootNode.add(fan_xs);
  913. this.htmlLayer[4].position = fan_xs.position;
  914. fan_xs.score = this.htmlLayer[4].jkd;
  915. this.initCylinderGeometry(fan_xs);
  916. // 改一下颜色
  917. this.changeObjectColor(fan_mhs, "green"); // 麻黄山
  918. this.changeObjectColor(fan_nss, "xd"); // 牛首山
  919. this.changeObjectColor(fan_qs, "检修"); // 青山
  920. this.changeObjectColor(fan_sbq, "white"); // 石板泉
  921. this.changeObjectColor(fan_xs, "运行"); // 香山
  922. // 风机存入数组
  923. fans.push(fan_mhs);
  924. fans.push(fan_nss);
  925. fans.push(fan_qs);
  926. fans.push(fan_sbq);
  927. fans.push(fan_xs);
  928. // 开始风机动画
  929. this.startFanAnimate();
  930. // 设置位置
  931. this.setEveryHTML();
  932. },
  933. // 设置光伏name
  934. setLightName(obj, name) {
  935. obj.name = name;
  936. obj.userData.name = name;
  937. let Box661 = obj.getObjectByName("Box661");
  938. let Box668 = obj.getObjectByName("Box668");
  939. let Box669 = obj.getObjectByName("Box669");
  940. let Box664 = obj.getObjectByName("Box664");
  941. let Box667 = obj.getObjectByName("Box667");
  942. let Box670 = obj.getObjectByName("Box670");
  943. Box661.name = `${name}_item_1`;
  944. Box661.userData.name = `${name}_item_1`;
  945. Box668.name = `${name}_item_2`;
  946. Box668.userData.name = `${name}_item_2`;
  947. Box669.name = `${name}_item_3`;
  948. Box669.userData.name = `${name}_item_3`;
  949. Box664.name = `${name}_item_4`;
  950. Box664.userData.name = `${name}_item_4`;
  951. Box667.name = `${name}_item_5`;
  952. Box667.userData.name = `${name}_item_5`;
  953. Box670.name = `${name}_item_6`;
  954. Box670.userData.name = `${name}_item_6`;
  955. },
  956. // 设置光伏位置 颜色
  957. setLightPosition(rootNode, obj) {
  958. obj.scale.set(200, 200, 200);
  959. // let Box660 = obj.getObjectByName("Box660");
  960. // let Box666 = obj.getObjectByName("Box666");
  961. // let Box662 = obj.getObjectByName("Box662");
  962. // let Box663 = obj.getObjectByName("Box663");
  963. // let Box659 = obj.getObjectByName("Box659");
  964. // let Box665 = obj.getObjectByName("Box665");
  965. // obj.remove(Box660);
  966. // obj.remove(Box666);
  967. // obj.remove(Box662);
  968. // obj.remove(Box663);
  969. // obj.remove(Box659);
  970. // obj.remove(Box665);
  971. // 大武口
  972. let light_dwk = obj.clone(true);
  973. light_dwk.position.set(-22, 0, -28); // -20 +30 // -2 0 -58 // -6, 0.13, -50 10, 0, -50
  974. // this.setLightName(light_dwk, "light_dwk");
  975. rootNode.add(light_dwk);
  976. this.htmlLayer[5].position = light_dwk.position;
  977. // 平罗
  978. let light_pl = obj.clone(true);
  979. light_pl.position.set(-26, 0.13, -25);
  980. // this.setLightName(light_pl, "light_pl");
  981. rootNode.add(light_pl);
  982. this.htmlLayer[6].position = light_pl.position;
  983. // 马场湖
  984. let light_mch = obj.clone(true);
  985. light_mch.position.set(-47, 2, 0);
  986. // this.setLightName(light_mch, "light_mch");
  987. rootNode.add(light_mch);
  988. this.htmlLayer[7].position = light_mch.position;
  989. // 宣和
  990. let light_xh = obj.clone(true);
  991. light_xh.position.set(-38, 4, 3);
  992. // this.setLightName(light_xh, "light_xh");
  993. rootNode.add(light_xh);
  994. this.htmlLayer[8].position = light_xh.position;
  995. // 海子井
  996. let light_hzj = obj.clone(true);
  997. light_hzj.position.set(-30, 2, 4);
  998. // this.setLightName(light_hzj, "light_hzj");
  999. rootNode.add(light_hzj);
  1000. this.htmlLayer[9].position = light_hzj.position;
  1001. // 改变颜色
  1002. this.changeElColor(light_dwk, "green"); // 大武口
  1003. this.changeElColor(light_pl, "离线"); // 平罗
  1004. this.changeElColor(light_mch, "yx"); // 马场湖
  1005. this.changeElColor(light_xh, "red"); // 宣和
  1006. this.changeElColor(light_hzj, "受累"); // 海子井
  1007. // 设置位置
  1008. this.setEveryHTML();
  1009. },
  1010. // 创建一个圆柱
  1011. initCylinderGeometry(obj, mode = "fan") {
  1012. let cr = 1.7;
  1013. let xyz = [5.75, 6.85, -0.63];
  1014. switch (mode) {
  1015. case "fan":
  1016. cr = 1.7;
  1017. xyz = [5.75, 6.85, -0.63];
  1018. break;
  1019. case "light":
  1020. cr = 3;
  1021. xyz = [5.75, 6.85, -0.63];
  1022. break;
  1023. default:
  1024. break;
  1025. }
  1026. let geometry = new THREE.CylinderGeometry(cr, cr, 4, 128, 1, true);
  1027. //加载纹理
  1028. let texture = new THREE.TextureLoader().load(
  1029. obj.score < 60
  1030. ? "static/3d/beam-texture-red.png"
  1031. : "static/3d/beam-texture.png"
  1032. );
  1033. texture.wrapS = texture.wrapT = THREE.RepeatWrapping; //每个都重复
  1034. texture.repeat.set(1, 1);
  1035. texture.needsUpdate = true;
  1036. let materials = [
  1037. //圆柱侧面材质,使用纹理贴图
  1038. new THREE.MeshBasicMaterial({
  1039. map: texture,
  1040. side: THREE.FrontSide,
  1041. transparent: true,
  1042. }),
  1043. //圆柱顶材质
  1044. new THREE.MeshBasicMaterial({
  1045. transparent: true,
  1046. opacity: 0,
  1047. side: THREE.DoubleSide,
  1048. // side: THREE.DoubleSide,THREE.FrontSide THREE.BackSide
  1049. }),
  1050. //圆柱底材质
  1051. new THREE.MeshBasicMaterial({
  1052. transparent: true,
  1053. opacity: 0,
  1054. side: THREE.DoubleSide,
  1055. }),
  1056. ];
  1057. let cylinder = new THREE.Mesh(geometry, materials);
  1058. cylinder.position.set(
  1059. obj.position.x + xyz[0],
  1060. obj.position.y + xyz[1],
  1061. obj.position.z + xyz[2]
  1062. );
  1063. scene.add(cylinder);
  1064. },
  1065. // 设置每一个html的位置
  1066. setEveryHTML() {
  1067. this.htmlLayer.forEach((value) => {
  1068. if (value.show && value.position) {
  1069. const screen = this.vector3ToScreen(value.position);
  1070. value.x = screen.x + value.ox;
  1071. value.y = screen.y + value.oy;
  1072. }
  1073. });
  1074. },
  1075. // 判断时候是可点击对象 返回null 或object
  1076. getClickObject(intersects) {
  1077. const names = ["fan"];
  1078. const namesRoot = ["fanvar"];
  1079. let x = true;
  1080. let obj = null;
  1081. for (let intersect of intersects) {
  1082. let temObj = intersect.object;
  1083. while (x) {
  1084. for (let name of names) {
  1085. if (temObj.name.indexOf(`${name}_`) == 0) {
  1086. obj = temObj;
  1087. x = false;
  1088. break;
  1089. }
  1090. }
  1091. if (x && temObj.parent) {
  1092. temObj = temObj.parent;
  1093. continue;
  1094. }
  1095. break;
  1096. }
  1097. }
  1098. if (obj) {
  1099. while (obj.parent && !namesRoot.includes(obj.parent.name)) {
  1100. obj = obj.parent;
  1101. }
  1102. }
  1103. if (obj && !namesRoot.includes(obj.parent.name)) {
  1104. obj = null;
  1105. }
  1106. return obj;
  1107. },
  1108. // 显示风机弹出层
  1109. showFanInfo(position) {
  1110. this.fanInfoLayer.position = {
  1111. x: position.x,
  1112. y: position.y,
  1113. z: position.z,
  1114. };
  1115. const screen = this.vector3ToScreen(this.fanInfoLayer.position);
  1116. this.fanInfoLayer.x = screen.x + this.fanInfoLayer.ox;
  1117. this.fanInfoLayer.y = screen.y + this.fanInfoLayer.oy;
  1118. this.fanInfoLayer.show = true;
  1119. },
  1120. // 隐藏风机弹出层
  1121. hideFanInfo() {
  1122. this.fanInfoLayer.show = false;
  1123. },
  1124. // 初始化云
  1125. initCloud() {},
  1126. // 初始化
  1127. initThree() {
  1128. this.initScene();
  1129. // this.initAxesHelper();
  1130. this.initCamera();
  1131. this.initRender();
  1132. this.initLight();
  1133. this.initControls();
  1134. this.initContent();
  1135. this.initCloud();
  1136. renderer.setAnimationLoop(this.animate);
  1137. },
  1138. // 动画
  1139. animate() {
  1140. renderer.render(scene, camera);
  1141. },
  1142. },
  1143. // 生命周期钩子
  1144. beforeCreate() {
  1145. // 创建前
  1146. },
  1147. created() {
  1148. // 创建后
  1149. },
  1150. beforeMount() {
  1151. // 渲染前
  1152. },
  1153. mounted() {
  1154. // 渲染后
  1155. this.initThree();
  1156. this.mapSource = this.data;
  1157. this.rinseData();
  1158. },
  1159. beforeUpdate() {
  1160. // 数据更新前
  1161. },
  1162. updated() {
  1163. // 数据更新后
  1164. },
  1165. beforeUnmount() {
  1166. // 销毁前
  1167. renderer.setAnimationLoop(null);
  1168. camera = null;
  1169. scene = null;
  1170. renderer = null;
  1171. },
  1172. watch: {
  1173. data(res) {
  1174. this.mapSource = res;
  1175. this.rinseData();
  1176. },
  1177. },
  1178. };
  1179. </script>
  1180. <style lang="less" scoped>
  1181. .three-model {
  1182. position: relative;
  1183. overflow: hidden;
  1184. // left: -112px;
  1185. .map-3d {
  1186. position: absolute;
  1187. width: 1118px;
  1188. height: 678px;
  1189. left: calc(50% - 559px);
  1190. top: calc(50% - 339px);
  1191. z-index: -1;
  1192. map {
  1193. width: 100%;
  1194. height: 100%;
  1195. }
  1196. }
  1197. .three-html-layer {
  1198. .three-html-dom {
  1199. position: absolute;
  1200. }
  1201. .fan-name {
  1202. display: inline-block;
  1203. height: 22px;
  1204. background: #222632;
  1205. border: 1px solid #646464;
  1206. border-radius: 10px;
  1207. padding: 0 10px;
  1208. font-size: 12px;
  1209. font-weight: 400;
  1210. color: #ffffff;
  1211. line-height: 22px;
  1212. }
  1213. .fan-can-click {
  1214. position: absolute;
  1215. background: #ff0000;
  1216. opacity: 0;
  1217. cursor: pointer;
  1218. }
  1219. .build-info {
  1220. width: 166px;
  1221. height: 33px;
  1222. // position: relative;
  1223. .build-info-close {
  1224. position: absolute;
  1225. width: 40px;
  1226. height: 40px;
  1227. top: -20px;
  1228. left: -20px;
  1229. border-radius: 50%;
  1230. background: #1a1f2fd8;
  1231. border: 1px solid #05bb4c;
  1232. box-shadow: 0px 8px 17px 1px #05bb4c66;
  1233. cursor: pointer;
  1234. color: @gray-l;
  1235. transition: all 0.3s;
  1236. display: flex;
  1237. align-items: center;
  1238. justify-content: center;
  1239. font-size: 14px;
  1240. &:hover {
  1241. color: #05bb4c;
  1242. }
  1243. }
  1244. .build-info-item {
  1245. width: 66px;
  1246. height: 66px;
  1247. border-radius: 50%;
  1248. background: #1a1f2fe5;
  1249. position: absolute;
  1250. &::after {
  1251. content: "";
  1252. position: absolute;
  1253. width: 44px;
  1254. height: 2px;
  1255. left: calc(50% - 22px);
  1256. top: calc(50% - 1px);
  1257. }
  1258. .build-info-item-num {
  1259. font-size: 22px;
  1260. font-weight: 500;
  1261. color: #ffffff;
  1262. text-align: center;
  1263. line-height: 1.2;
  1264. margin-top: 6px;
  1265. margin-bottom: 3px;
  1266. }
  1267. .build-info-item-text {
  1268. font-size: 12px;
  1269. color: #ffffff;
  1270. font-weight: 400;
  1271. text-align: center;
  1272. }
  1273. &.blue {
  1274. border: 2px solid #1da0d7;
  1275. &::after {
  1276. background: #1da0d7;
  1277. }
  1278. }
  1279. &.red {
  1280. border: 2px solid #ff0000;
  1281. &::after {
  1282. background: #ff0000;
  1283. }
  1284. }
  1285. &.green {
  1286. border: 2px solid #05bb4c;
  1287. &::after {
  1288. background: #05bb4c;
  1289. }
  1290. }
  1291. &.purple {
  1292. border: 2px solid #323e6f;
  1293. &::after {
  1294. background: #323e6f;
  1295. }
  1296. }
  1297. &.orange {
  1298. border: 2px solid #db5520;
  1299. &::after {
  1300. background: #db5520;
  1301. }
  1302. }
  1303. &.yellow {
  1304. border: 2px solid #edb32f;
  1305. &::after {
  1306. background: #edb32f;
  1307. }
  1308. }
  1309. }
  1310. }
  1311. }
  1312. }
  1313. </style>