mainWind.threejs.ts 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  1. import * as THREE from 'three';
  2. import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
  3. import Smoke from '/@/views/vent/comment/threejs/Smoke';
  4. import { PathPointList, PathGeometry } from 'three.path';
  5. import gsap from 'gsap';
  6. class mainWindRect {
  7. model;
  8. modelName = 'main';
  9. group: THREE.Group | null = null; // 主通风机场景
  10. motorGroup1: THREE.Group | null = null; //电机
  11. motorGroup2: THREE.Group | null = null; //电机
  12. airJin1: THREE.Mesh | null = null; //风向箭头
  13. airJin2: THREE.Mesh | null = null; //风向箭头
  14. airChu1: THREE.Mesh | null = null; //风向箭头
  15. airChu2: THREE.Mesh | null = null; //风向箭头
  16. gearFront = {
  17. gear1: null, //扇叶
  18. gear2: null, //扇叶
  19. gear1Direction: -1,
  20. gear2Direction: 1,
  21. gearFrameId: undefined,
  22. gearRotateFactor: 0.5,
  23. endGearRotateFactor: 3,
  24. };
  25. gearBack = {
  26. gear1: null, //扇叶
  27. gear2: null, //扇叶
  28. gear1Direction: -1, // 扇叶转动方向
  29. gear2Direction: 1, // 扇叶转动方向
  30. gearFrameId: undefined,
  31. gearRotateFactor: 0.5, // 扇叶转动因素
  32. endGearRotateFactor: 3, // 扇叶最终转动速度因素
  33. };
  34. oldMaterial: THREE.Material = new THREE.MeshStandardMaterial();
  35. // smoke;
  36. frontSmoke: Smoke | null = null; // 前面风流对象
  37. backSmoke: Smoke | null = null; // 后面风流对象
  38. player1; // 视频播放器
  39. playerStartClickTime1 = new Date().getTime();
  40. frontWindowGroup;
  41. backWindowGroup;
  42. windowAngle = 0;
  43. fbmAnimationClip: THREE.AnimationClip | null = null;
  44. fbmMixers: THREE.AnimationMixer | null = null;
  45. fbmOpenAction: THREE.AnimationAction | null = null;
  46. clock = new THREE.Clock();
  47. material;
  48. airTexture;
  49. offset = 0;
  50. direction = 0; // -1 代表反向,1代表正向
  51. arrowMesh;
  52. constructor(model, playerVal1) {
  53. this.model = model;
  54. this.player1 = playerVal1;
  55. }
  56. // 添加 cssObject
  57. addCssText() {
  58. if (!this.group) {
  59. return;
  60. }
  61. const ztfjGroup = this.group.getObjectByName('ztfj');
  62. if (!this.group.getObjectByName('monitorText1')) {
  63. const worldPosition = new THREE.Vector3();
  64. ztfjGroup?.getObjectByName('Cylinder1042')?.getWorldPosition(worldPosition);
  65. const element = document.getElementById('inputBox') as HTMLElement;
  66. if (element) {
  67. const mainCSS3D = new CSS3DObject(element);
  68. mainCSS3D.name = 'monitorText1';
  69. mainCSS3D.scale.set(0.09, 0.09, 0.09);
  70. // mainCSS3D.position.set(23.78, 18.18, -6.85);
  71. mainCSS3D.position.set(worldPosition.x + 12, worldPosition.y - 10, worldPosition.z - 20);
  72. mainCSS3D.lookAt(worldPosition.x + 12, worldPosition.y - 0, worldPosition.z + 2);
  73. this.group.add(mainCSS3D);
  74. }
  75. }
  76. if (!this.group.getObjectByName('monitorText2')) {
  77. const worldPosition = new THREE.Vector3();
  78. ztfjGroup?.getObjectByName('Cylinder396')?.getWorldPosition(worldPosition);
  79. const element = document.getElementById('inputBox1') as HTMLElement;
  80. if (element) {
  81. const mainCSS3D = new CSS3DObject(element);
  82. mainCSS3D.name = 'monitorText2';
  83. mainCSS3D.scale.set(0.09, 0.09, 0.09);
  84. // mainCSS3D.position.set(23.78, 18.18, 16.82);
  85. mainCSS3D.position.set(worldPosition.x + 12, worldPosition.y - 10, worldPosition.z - 20);
  86. mainCSS3D.lookAt(worldPosition.x + 12, worldPosition.y - 0, worldPosition.z + 2);
  87. // mainCSS3D.lookAt(23.78, 20, 20.82);
  88. this.group.add(mainCSS3D);
  89. }
  90. }
  91. if (!this.group.getObjectByName('monitorText4')) {
  92. const worldPosition = new THREE.Vector3();
  93. const fbmGroup = this.group?.getObjectByName('fbm') as THREE.Group;
  94. if (fbmGroup) {
  95. fbmGroup?.getObjectByName('Box022')?.getWorldPosition(worldPosition);
  96. const element = document.getElementById('fbm') as HTMLElement;
  97. if (element) {
  98. const mainCSS3D = new CSS3DObject(element);
  99. mainCSS3D.name = 'monitorText4';
  100. mainCSS3D.scale.set(0.07, 0.07, 0.07);
  101. mainCSS3D.position.set(worldPosition.x + 20, worldPosition.y - 8, worldPosition.z - 20);
  102. mainCSS3D.lookAt(worldPosition.x + 20, worldPosition.y - 0, worldPosition.z + 2);
  103. this.group.add(mainCSS3D);
  104. }
  105. }
  106. }
  107. }
  108. clearCssText() {
  109. if (this.group) {
  110. const mainCSS3D1 = this.group.getObjectByName('monitorText1');
  111. const mainCSS3D2 = this.group.getObjectByName('monitorText2');
  112. if (mainCSS3D1) this.group.remove(mainCSS3D1);
  113. if (mainCSS3D2) this.group.remove(mainCSS3D2);
  114. }
  115. }
  116. addEcharts() {
  117. const echartsBox = document.getElementById('fan-echarts');
  118. if (echartsBox) {
  119. const canvasObj = echartsBox.getElementsByTagName('canvas')[0];
  120. // 将canvas 纹理转换为材质
  121. const echartsMap = new THREE.CanvasTexture(canvasObj); // 关键一步
  122. const echartsMaterial = new THREE.MeshBasicMaterial({
  123. map: echartsMap, // 设置纹理贴图
  124. transparent: true,
  125. side: THREE.FrontSide, // 这里是双面渲染的意思
  126. });
  127. echartsMaterial.blending = THREE.CustomBlending;
  128. const monitorPlane = this.group?.getObjectByName('monitorEcharts');
  129. if (monitorPlane) {
  130. monitorPlane.material = echartsMaterial;
  131. } else {
  132. const planeGeometry = new THREE.PlaneGeometry(17.6, 9.9); // 平面3维几何体PlaneGeometry
  133. const planeMesh = new THREE.Mesh(planeGeometry, echartsMaterial);
  134. planeMesh.name = 'monitorEcharts';
  135. planeMesh.scale.set(1, 1, 1);
  136. planeMesh.position.set(-47.38, 13.227, -21.79);
  137. this.group?.add(planeMesh);
  138. }
  139. }
  140. }
  141. initAnimation() {}
  142. startAnimation() {}
  143. /* 更新动画 */
  144. render() {
  145. if (!this.model) {
  146. return;
  147. }
  148. if (this.fbmMixers) this.fbmMixers?.update(1 / 25);
  149. if (this.airTexture) {
  150. this.airTexture.offset.x = this.offset;
  151. this.offset -= this.clock.getDelta() * 2;
  152. }
  153. }
  154. /* 点击风窗,风窗全屏 */
  155. mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
  156. // 判断是否点击到视频
  157. intersects.find((intersect) => {
  158. const mesh = intersect.object;
  159. if (mesh.name === 'player1') {
  160. if (new Date().getTime() - this.playerStartClickTime1 < 400) {
  161. // 双击,视频放大
  162. if (this.player1) {
  163. this.player1.requestFullscreen();
  164. }
  165. }
  166. this.playerStartClickTime1 = new Date().getTime();
  167. return true;
  168. }
  169. return false;
  170. });
  171. }
  172. mouseUpModel() {}
  173. async setDeviceFrequency(deviceType, state, frequencyVal?) {
  174. // 调节频率
  175. if (frequencyVal) {
  176. this.resetSmokeParam(deviceType, frequencyVal, 0);
  177. }
  178. this.openOrCloseValve(deviceType, state, 0);
  179. this.startGearAnimation(deviceType, state, '', 0);
  180. if (deviceType === 'front') {
  181. this.frontSmoke?.startSmoke();
  182. } else {
  183. this.backSmoke?.startSmoke();
  184. }
  185. setTimeout(() => {
  186. this.lookMotor(deviceType, state, 10);
  187. }, 2000);
  188. }
  189. async openDevice(deviceType, smokeDirection, frequencyVal, duration?) {
  190. if (smokeDirection) {
  191. this.setSmokeDirection(deviceType, smokeDirection);
  192. }
  193. let smoke;
  194. if (deviceType === 'front') {
  195. smoke = this.frontSmoke;
  196. } else {
  197. smoke = this.backSmoke;
  198. }
  199. if (!smoke.frameId) {
  200. await this.lookMotor(deviceType, 'open', duration);
  201. await this.openOrCloseValve(deviceType, 'open', duration);
  202. this.startGearAnimation(deviceType, 'open', smokeDirection, frequencyVal, duration);
  203. smoke.startSmoke(duration);
  204. }
  205. }
  206. async closeDevice(deviceType, flag = true) {
  207. let smoke;
  208. if (deviceType === 'front') {
  209. smoke = this.frontSmoke;
  210. } else if (deviceType === 'back') {
  211. smoke = this.backSmoke;
  212. }
  213. if (smoke && smoke.frameId) {
  214. if (flag) {
  215. smoke.stopSmoke();
  216. await this.openOrCloseValve(deviceType, 'close');
  217. this.startGearAnimation(deviceType, 'close', '', null);
  218. await this.lookMotor(deviceType, 'close');
  219. } else {
  220. smoke.stopSmoke(0);
  221. await this.openOrCloseValve(deviceType, 'close', 0);
  222. this.startGearAnimation(deviceType, 'close', '', null, 0);
  223. await this.lookMotor(deviceType, 'close', 0);
  224. }
  225. }
  226. }
  227. async setSmokeDirection(deviceType, smokeDirection) {
  228. let smoke;
  229. const pathPoints: THREE.Vector3[] = [];
  230. const windowPositivePath = [
  231. {
  232. path0: new THREE.Vector3(4.441, 20.267, 3.614),
  233. path1: new THREE.Vector3(5.041, 6.806, 3.614),
  234. isSpread: true,
  235. spreadDirection: -1, //
  236. },
  237. {
  238. path0: new THREE.Vector3(7.441, 0.806, 3.614),
  239. path1: new THREE.Vector3(41.583, 1.485, 3.614),
  240. isSpread: false,
  241. spreadDirection: 0, //
  242. },
  243. {
  244. path0: new THREE.Vector3(41.583, 1.485, 3.614),
  245. path1: new THREE.Vector3(42.741, 5.364, 3.614),
  246. isSpread: false,
  247. spreadDirection: 0,
  248. },
  249. {
  250. path0: new THREE.Vector3(42.741, 5.364, 3.614),
  251. path1: new THREE.Vector3(44.741, 17.267, 3.614),
  252. isSpread: true,
  253. spreadDirection: 1, // 1是由小变大(出),-1是由大变小(进)
  254. },
  255. ];
  256. const windowInversePath = [
  257. {
  258. path0: new THREE.Vector3(44.741, 17.267, 3.614),
  259. path1: new THREE.Vector3(42.741, 5.364, 3.614),
  260. isSpread: true,
  261. spreadDirection: -1, //
  262. },
  263. {
  264. path0: new THREE.Vector3(42.741, 5.364, 3.614),
  265. path1: new THREE.Vector3(41.583, 1.485, 3.614),
  266. isSpread: false,
  267. spreadDirection: 0, //
  268. },
  269. {
  270. path0: new THREE.Vector3(41.583, 1.485, 3.614),
  271. path1: new THREE.Vector3(7.441, 0.806, 3.614),
  272. isSpread: false,
  273. spreadDirection: 0, // 1是由小变大,-1是由大变小
  274. },
  275. {
  276. path0: new THREE.Vector3(4.441, 17.267, 3.614),
  277. path1: new THREE.Vector3(5.041, 6.806, 3.614),
  278. isSpread: true,
  279. spreadDirection: 1, //
  280. },
  281. ];
  282. const tubPositivePath = [
  283. {
  284. path0: new THREE.Vector3(7.441, 0.806, 3.614),
  285. path1: new THREE.Vector3(44.583, 1.485, 3.614),
  286. isSpread: false,
  287. spreadDirection: 0, //
  288. },
  289. {
  290. path0: new THREE.Vector3(44.583, 1.485, 3.614),
  291. path1: new THREE.Vector3(45.741, 5.364, 3.614),
  292. isSpread: false,
  293. spreadDirection: 0,
  294. },
  295. {
  296. path0: new THREE.Vector3(45.741, 5.364, 3.614),
  297. path1: new THREE.Vector3(47.741, 17.267, 3.614),
  298. isSpread: true,
  299. spreadDirection: 1, // 1是由小变大(出),-1是由大变小(进)
  300. },
  301. ];
  302. const tubInversePath = [
  303. {
  304. path0: new THREE.Vector3(47.741, 17.267, 3.614),
  305. path1: new THREE.Vector3(45.741, 5.364, 3.614),
  306. isSpread: true,
  307. spreadDirection: -1, //
  308. },
  309. {
  310. path0: new THREE.Vector3(45.741, 5.364, 3.614),
  311. path1: new THREE.Vector3(44.583, 1.485, 3.614),
  312. isSpread: false,
  313. spreadDirection: 0, //
  314. },
  315. {
  316. path0: new THREE.Vector3(44.583, 1.485, 3.614),
  317. path1: new THREE.Vector3(7.441, 0.806, 3.614),
  318. isSpread: false,
  319. spreadDirection: 0, // 1是由小变大,-1是由大变小
  320. },
  321. ];
  322. const getPathPoint = () => {
  323. this.arrowMesh = this.group?.getObjectByName('arrow');
  324. if (this.arrowMesh) return;
  325. pathPoints.push(new THREE.Vector3(16.441, 1.485, 2.614), new THREE.Vector3(35.583, 1.485, 2.614));
  326. const pathPointList = new PathPointList();
  327. const up = new THREE.Vector3(0, 0, 1);
  328. pathPointList.set(pathPoints, 0, 0, up, false);
  329. const geometry = new PathGeometry(pathPoints.length, false);
  330. geometry.update(pathPointList, {
  331. width: 2,
  332. arrow: false,
  333. });
  334. this.arrowMesh = new THREE.Mesh(geometry, this.material);
  335. this.arrowMesh.name = 'arrow';
  336. this.group?.add(this.arrowMesh);
  337. };
  338. if (deviceType === 'front') {
  339. smoke = this.frontSmoke;
  340. } else if (deviceType === 'back') {
  341. smoke = this.backSmoke;
  342. }
  343. switch (smokeDirection) {
  344. case 'tubPositivePath': // 风筒正
  345. smoke.setPath(tubPositivePath);
  346. if (this.direction !== 1) {
  347. this.direction = 1;
  348. this.airTexture.repeat.x = 1;
  349. }
  350. break;
  351. case 'tubInversePath': // 风筒反
  352. smoke.setPath(tubInversePath);
  353. if (this.direction !== -1) {
  354. this.direction = -1;
  355. this.airTexture.repeat.x = -1;
  356. }
  357. break;
  358. case 'windowPositivePath': // 风窗正
  359. smoke.setPath(windowPositivePath);
  360. if (this.direction !== 1) {
  361. this.direction = 1;
  362. this.airTexture.repeat.x = 1;
  363. }
  364. break;
  365. case 'windowInversePath': // 风窗反
  366. smoke.setPath(windowInversePath);
  367. if (this.direction !== -1) {
  368. this.direction = -1;
  369. this.airTexture.repeat.x = -1;
  370. }
  371. break;
  372. }
  373. getPathPoint();
  374. if (deviceType === 'front') {
  375. if (this.arrowMesh && this.arrowMesh.position.z !== 2.31) this.arrowMesh.position.set(-2.51, 5.51, 13.25);
  376. } else {
  377. if (this.arrowMesh && this.arrowMesh.position.z !== -12.99) this.arrowMesh.position.set(-2.2, 5.51, -2.8);
  378. }
  379. }
  380. /* 播放气流动画 */
  381. /**
  382. *
  383. * @param controlType // 设备控制类型
  384. * @param deviceType //前后风机
  385. * @param frequencyVal // 风机运行频率
  386. * @param state // 打开、关闭状态
  387. */
  388. async playSmoke(controlType, deviceType, frequencyVal, state, smokeDirection) {
  389. if (frequencyVal) {
  390. this.resetSmokeParam(deviceType, frequencyVal);
  391. }
  392. if (controlType === 'startSmoke') {
  393. if (state === 'stop') {
  394. await this.closeDevice(deviceType);
  395. } else {
  396. // 开启时需要设置方向
  397. await this.openDevice(deviceType, smokeDirection, frequencyVal);
  398. }
  399. } else if (controlType === 'changeDirection') {
  400. // 改变扇叶转动方向、反风
  401. this.startGearAnimation(deviceType, 'changeDirection', smokeDirection, frequencyVal);
  402. let smoke;
  403. if (deviceType === 'front') {
  404. smoke = this.frontSmoke;
  405. } else {
  406. smoke = this.backSmoke;
  407. }
  408. if (smoke && smoke.frameId) {
  409. await smoke.stopSmoke();
  410. await this.setSmokeDirection(deviceType, smokeDirection);
  411. smoke.startSmoke();
  412. }
  413. } else if (controlType === 'frequency') {
  414. this.startGearAnimation(deviceType, 'frequency', smokeDirection, frequencyVal);
  415. } else if (controlType === 'initiatePlay') {
  416. this.openDevice(deviceType, smokeDirection, frequencyVal, 0);
  417. } else if (controlType === 'changeSmoke') {
  418. //
  419. }
  420. }
  421. stopSmoke() {
  422. this.closeDevice('front', false);
  423. this.closeDevice('back', false);
  424. }
  425. /* 打开或关闭蝶阀 */
  426. openOrCloseValve(deviceType, flag, duration = 3) {
  427. const ztfjGroup = this.group?.getObjectByName('ztfj');
  428. return new Promise((resolve) => {
  429. let diefa;
  430. if (deviceType == 'front') {
  431. diefa = ztfjGroup?.getObjectByName('butterfly_valve001') as THREE.Mesh;
  432. } else {
  433. diefa = ztfjGroup?.getObjectByName('butterfly_valve002') as THREE.Mesh;
  434. }
  435. let rotationY;
  436. if (flag == 'open') {
  437. rotationY = 0;
  438. } else {
  439. rotationY = Math.PI / 2;
  440. }
  441. if (diefa) {
  442. gsap.to(diefa.rotation, {
  443. y: rotationY,
  444. duration: duration,
  445. ease: 'none',
  446. onComplete: function () {
  447. resolve(null);
  448. },
  449. });
  450. }
  451. });
  452. }
  453. /* 风流调频, 范围1-50 */
  454. // opacityFactor (0.4 300)
  455. // life 最小 300, 最大 50
  456. // speedFactor 最大0, 最小100
  457. resetSmokeParam(deviceType, frequency, duration = 5) {
  458. if (frequency < 1) frequency = 1;
  459. if (frequency > 50) frequency = 50;
  460. let smoke;
  461. if (deviceType === 'front') {
  462. smoke = this.frontSmoke;
  463. } else {
  464. smoke = this.backSmoke;
  465. }
  466. const opacityFactor = (frequency / 50) * 0.8;
  467. duration = (Number(Math.abs(smoke.opacityFactor - opacityFactor).toFixed(1)) / 0.8) * 5;
  468. const life = (-250 / 50) * frequency + 300;
  469. gsap.to(smoke, {
  470. opacityFactor: opacityFactor,
  471. life: life,
  472. duration: duration,
  473. ease: 'easeInCirc',
  474. overwrite: true,
  475. });
  476. }
  477. /* 显示电机 */
  478. lookMotor(deviceType, flag, duration = 5) {
  479. return new Promise((resolve) => {
  480. const ztfjGroup = this.group?.getObjectByName('ztfj');
  481. let mesh, mesh1, mesh2, motorGroup;
  482. mesh1 = ztfjGroup?.getObjectByName('TWO00'); //前
  483. mesh2 = ztfjGroup?.getObjectByName('ONE00'); //后
  484. if (deviceType == 'front') {
  485. mesh = mesh1;
  486. motorGroup = this.motorGroup2;
  487. } else {
  488. mesh = mesh2;
  489. motorGroup = this.motorGroup1;
  490. }
  491. if (mesh && motorGroup) {
  492. if (flag == 'open') {
  493. mesh.material.depthWrite = false;
  494. mesh.material.depthTest = false;
  495. motorGroup.visible = true;
  496. gsap.to(mesh.material, {
  497. opacity: 0.1,
  498. duration: duration,
  499. overwrite: true,
  500. onComplete: function () {
  501. // mesh.material.color = '#000';
  502. resolve(null);
  503. },
  504. });
  505. } else {
  506. const opacity = mesh.material.opacity;
  507. Object.assign(mesh.material, this.oldMaterial, { opacity: opacity });
  508. mesh.material.depthWrite = true;
  509. mesh.material.depthTest = true;
  510. gsap.to(mesh.material, {
  511. opacity: 1,
  512. duration: 1,
  513. overwrite: true,
  514. onComplete: function () {
  515. resolve(null);
  516. },
  517. });
  518. }
  519. }
  520. });
  521. }
  522. /* 齿轮转动动画 1 - 50 最大3 */
  523. startGearAnimation(deviceType, flag, smokeDirection, frequencyVal, duration = 8) {
  524. let gearObj, gearDirection;
  525. if (deviceType === 'front') {
  526. gearObj = this.gearFront;
  527. } else {
  528. gearObj = this.gearBack;
  529. }
  530. if (smokeDirection === 'tubPositivePath') {
  531. gearDirection = 1;
  532. } else if (smokeDirection === 'tubInversePath') {
  533. gearDirection = -1;
  534. }
  535. if (frequencyVal) {
  536. const endGearRotateFactor = (3 / 50) * frequencyVal;
  537. duration = (8 / 3) * Math.abs(gearObj.endGearRotateFactor - endGearRotateFactor);
  538. gearObj.endGearRotateFactor = endGearRotateFactor;
  539. }
  540. const gearAnimation = () => {
  541. gsap.to(gearObj, {
  542. gearRotateFactor: gearObj.endGearRotateFactor,
  543. duration: duration,
  544. ease: 'easeInCubic',
  545. repeat: 0,
  546. overwrite: true,
  547. });
  548. const clock = new THREE.Clock(); // 时钟
  549. const h = () => {
  550. if (gearObj.gear1 && gearObj.gear2) {
  551. gearObj.gearFrameId = requestAnimationFrame(h);
  552. const dt = clock.getDelta();
  553. gearObj.gear1.rotation.x += dt * gearObj.gearRotateFactor * gearObj.gear1Direction;
  554. gearObj.gear2.rotation.x += dt * gearObj.gearRotateFactor * gearObj.gear2Direction;
  555. }
  556. };
  557. h();
  558. };
  559. if (flag === 'changeDirection') {
  560. if (gearDirection == -1 * gearObj.gear1Direction) {
  561. // 齿轮正在转,需要停止后再反方向转
  562. gsap.to(gearObj, {
  563. gearRotateFactor: 0,
  564. duration: duration,
  565. ease: 'easeInCubic',
  566. repeat: 0,
  567. onComplete: function () {
  568. window.cancelAnimationFrame(gearObj.gearFrameId);
  569. gearObj.gearFrameId = undefined;
  570. gearObj.gear1Direction = -1 * gearObj.gear1Direction;
  571. gearObj.gear2Direction = -1 * gearObj.gear2Direction;
  572. gearAnimation();
  573. },
  574. });
  575. }
  576. } else if (flag === 'open') {
  577. gearObj.gear1Direction = gearDirection;
  578. gearObj.gear2Direction = -1 * gearDirection;
  579. gearAnimation();
  580. } else if (flag === 'close') {
  581. gsap.to(gearObj, {
  582. gearRotateFactor: 0,
  583. duration: duration,
  584. ease: 'easeInCubic',
  585. repeat: 0,
  586. overwrite: true,
  587. onComplete: function () {
  588. window.cancelAnimationFrame(gearObj.gearFrameId);
  589. gearObj.gearFrameId = undefined;
  590. },
  591. });
  592. } else if (flag === 'frequency') {
  593. gsap.to(gearObj, {
  594. gearRotateFactor: gearObj.endGearRotateFactor,
  595. duration: duration,
  596. ease: 'easeInCubic',
  597. repeat: 0,
  598. overwrite: true,
  599. });
  600. }
  601. }
  602. /* 初始化口上面的气体 */
  603. initSmokeMass() {
  604. if (!this.frontSmoke) {
  605. this.frontSmoke = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.4, 1.8, 100);
  606. }
  607. if (!this.backSmoke) {
  608. this.backSmoke = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.4, 1.8, 100);
  609. }
  610. }
  611. /* 设置气流位置 */
  612. async setSmokePosition() {
  613. if (this.frontSmoke) {
  614. await this.frontSmoke.setPoints();
  615. this.frontSmoke.points.name = 'frontSmoke';
  616. this.group?.add(this.frontSmoke.points);
  617. // this.frontSmoke.points.position.set(-2.51, 2.51, 8.25);
  618. this.frontSmoke.points.position.set(-2.51, 2.51, 13.25);
  619. }
  620. if (this.backSmoke) {
  621. await this.backSmoke.setPoints();
  622. this.backSmoke.points.name = 'backSmoke';
  623. this.group?.add(this.backSmoke.points);
  624. // this.backSmoke.points.position.set(-2.2, 3.13, -7.8);
  625. this.backSmoke.points.position.set(-2.2, 3.13, -2.8);
  626. }
  627. }
  628. /** 初始化电机 */
  629. async initMotor() {
  630. // 前电机
  631. const motorGltf1 = await this.model.setGLTFModel('dj1');
  632. this.motorGroup1 = motorGltf1[0] as THREE.Group;
  633. this.motorGroup1?.position.set(0, 0, 5);
  634. this.motorGroup1.visible = false;
  635. this.motorGroup1.traverse((item) => {
  636. if (item instanceof THREE.Object3D) {
  637. item.renderOrder = -1;
  638. if (item.name === 'fan_blade003') {
  639. // @ts-ignore
  640. this.gearBack.gear1 = item as THREE.Group;
  641. } else if (item.name === 'fan_blade005') {
  642. // @ts-ignore
  643. this.gearBack.gear2 = item as THREE.Group;
  644. }
  645. }
  646. });
  647. this.motorGroup1.renderOrder = -1;
  648. this.group?.add(this.motorGroup1);
  649. // 后电机
  650. const motorGltf2 = await this.model.setGLTFModel('dj2');
  651. this.motorGroup2 = motorGltf2[0] as THREE.Group;
  652. this.motorGroup2?.position.set(0, 0, 5);
  653. this.motorGroup2.visible = false;
  654. this.motorGroup2.traverse((item) => {
  655. if (item instanceof THREE.Object3D) {
  656. item.renderOrder = -1;
  657. if (item.name === 'fan_blade007') {
  658. // @ts-ignore
  659. this.gearFront.gear1 = item as THREE.Group;
  660. } else if (item.name === 'fan_blade006') {
  661. // @ts-ignore
  662. this.gearFront.gear2 = item as THREE.Group;
  663. }
  664. }
  665. });
  666. this.motorGroup2.renderOrder = -1;
  667. this.group?.add(this.motorGroup2);
  668. }
  669. openOrCloseWindow(deviceType, flag) {
  670. const _this = this;
  671. let endAngle = 0,
  672. windowGroup;
  673. if (deviceType === 'front') {
  674. windowGroup = this.frontWindowGroup;
  675. }
  676. if (deviceType === 'back') {
  677. windowGroup = this.backWindowGroup;
  678. }
  679. if (flag == 'openWindow') {
  680. // 打开风窗
  681. endAngle = 1;
  682. } else {
  683. // 关闭风窗
  684. endAngle = 0;
  685. }
  686. if (windowGroup)
  687. gsap.to(this, {
  688. windowAngle: endAngle,
  689. duration: Math.abs(endAngle - this.windowAngle) * 10,
  690. ease: 'none',
  691. onUpdate: function () {
  692. windowGroup.children.forEach((mesh) => {
  693. mesh.rotation.z = _this.windowAngle;
  694. });
  695. },
  696. });
  697. }
  698. /** 初始化风窗 */
  699. initWindow() {
  700. if (!this.group) return;
  701. this.frontWindowGroup = new THREE.Group();
  702. this.frontWindowGroup.name = 'frontWindowGroup';
  703. this.backWindowGroup = new THREE.Group();
  704. this.backWindowGroup.name = 'backWindowGroup';
  705. if (this.group && this.group?.children.length > 0) {
  706. for (let i = this.group?.children.length - 1; i >= 0; i--) {
  707. const obj = this.group?.children[i];
  708. if (obj.type === 'Mesh' && obj.name && obj.name.startsWith('TC')) {
  709. const mesh = obj.clone();
  710. if (obj.name.startsWith('TC1')) {
  711. this.backWindowGroup.add(mesh);
  712. } else if (obj.name.startsWith('TC2')) {
  713. this.frontWindowGroup.add(mesh);
  714. }
  715. obj.removeFromParent();
  716. this.group?.remove(obj);
  717. }
  718. }
  719. }
  720. this.group?.add(this.backWindowGroup);
  721. this.group?.add(this.frontWindowGroup);
  722. }
  723. initFbmAnimation() {
  724. const fbmGroup = this.group?.getObjectByName('fbm') as THREE.Group;
  725. if (fbmGroup) {
  726. this.fbmAnimationClip = fbmGroup.animations[0];
  727. this.fbmMixers = new THREE.AnimationMixer(fbmGroup);
  728. this.fbmOpenAction = this.fbmMixers.clipAction(this.fbmAnimationClip);
  729. this.fbmOpenAction.loop = THREE.LoopOnce;
  730. this.fbmOpenAction.clampWhenFinished = true;
  731. this.fbmMixers.timeScale = 0.15;
  732. }
  733. }
  734. playAnimation(flag, duration?) {
  735. if (this.fbmOpenAction && this.fbmMixers && this.fbmAnimationClip) {
  736. if (duration == 0) {
  737. if (flag == 'open') {
  738. this.fbmOpenAction.reset();
  739. this.fbmOpenAction.time = this.fbmAnimationClip.duration;
  740. this.fbmOpenAction.timeScale = 1;
  741. } else {
  742. this.fbmOpenAction.reset();
  743. this.fbmOpenAction.time = 0;
  744. this.fbmOpenAction.timeScale = -1;
  745. }
  746. this.fbmOpenAction.play();
  747. } else {
  748. if (flag == 'open') {
  749. this.fbmOpenAction.reset();
  750. this.fbmOpenAction.time = 0;
  751. this.fbmOpenAction.timeScale = 1;
  752. this.fbmOpenAction.play();
  753. } else {
  754. this.fbmOpenAction.reset();
  755. this.fbmOpenAction.time = this.fbmAnimationClip.duration;
  756. this.fbmOpenAction.timeScale = -1;
  757. this.fbmOpenAction.play();
  758. }
  759. }
  760. }
  761. }
  762. mountedThree() {
  763. this.group = new THREE.Group();
  764. return new Promise(async (resolve) => {
  765. this.model.setGLTFModel(['bg1', 'fbm', 'ztfj', 'ztfj-fc'], this.group).then(async () => {
  766. // this.group = gltf[0];
  767. this.group?.position.set(-0.44, 19.88, 22.37);
  768. this.initSmokeMass();
  769. await this.setSmokePosition();
  770. const ztfjGroup = this.group?.getObjectByName('ztfj') as THREE.Group;
  771. const fbmGroup = this.group?.getObjectByName('fbm') as THREE.Group;
  772. const fcGroup = this.group?.getObjectByName('ztfj-fc') as THREE.Group;
  773. if (ztfjGroup) {
  774. ztfjGroup.position.z = ztfjGroup.position.z + 5;
  775. const airJinGroup = ztfjGroup.getObjectByName('JianTou1_Jin') as THREE.Object3D;
  776. const airChuGroup = ztfjGroup.getObjectByName('JianTou2_Chu') as THREE.Object3D;
  777. this.airJin1 = airJinGroup.getObjectByName('Jin_1') as THREE.Mesh;
  778. this.airJin2 = airJinGroup.getObjectByName('Jin_2') as THREE.Mesh;
  779. this.airChu1 = airChuGroup.getObjectByName('Chu_1') as THREE.Mesh;
  780. this.airChu2 = airChuGroup.getObjectByName('Chu_2') as THREE.Mesh;
  781. }
  782. if (fbmGroup) fbmGroup.position.z = fbmGroup.position.z + 5;
  783. if (fcGroup) fcGroup.position.z = fcGroup.position.z + 5;
  784. const mesh = ztfjGroup?.getObjectByName('jizu06') as THREE.Mesh; //前
  785. if (mesh && mesh.material) this.oldMaterial = mesh.material as THREE.MeshStandardMaterial;
  786. await this.initMotor();
  787. resolve(null);
  788. this.initWindow();
  789. this.initFbmAnimation();
  790. const loader = new THREE.TextureLoader();
  791. this.airTexture = loader.load('/model/img/air.png');
  792. this.airTexture.wrapS = THREE.RepeatWrapping;
  793. this.airTexture.repeat.set(1, 1.2);
  794. this.airTexture.offset.y = 0;
  795. this.airTexture.matrix.scale(0.5, 0.5);
  796. this.airTexture.needsUpdate = true;
  797. this.material = new THREE.MeshBasicMaterial({
  798. map: this.airTexture,
  799. transparent: true,
  800. side: THREE.FrontSide,
  801. });
  802. this.clock.start();
  803. });
  804. });
  805. }
  806. destroy() {
  807. this.frontSmoke.clearSmoke();
  808. this.backSmoke.clearSmoke();
  809. const fbmGroup = this.group?.getObjectByName('fbm') as THREE.Group;
  810. if (this.fbmMixers && this.fbmAnimationClip && this.fbmOpenAction && fbmGroup) {
  811. this.fbmMixers.uncacheClip(this.fbmAnimationClip);
  812. this.fbmMixers.uncacheAction(this.fbmOpenAction.getClip(), fbmGroup);
  813. this.fbmMixers.uncacheRoot(fbmGroup);
  814. this.fbmAnimationClip.tracks = [];
  815. this.fbmAnimationClip = undefined;
  816. this.fbmOpenAction = undefined;
  817. }
  818. this.model.clearGroup(this.motorGroup1);
  819. this.model.clearGroup(this.motorGroup2);
  820. this.model.clearGroup(this.group);
  821. this.motorGroup1 = undefined;
  822. this.motorGroup2 = undefined;
  823. this.gearFront.gear1 = undefined;
  824. this.gearFront.gear2 = undefined;
  825. this.gearBack.gear1 = undefined;
  826. this.gearBack.gear2 = undefined;
  827. this.frontSmoke = undefined;
  828. this.backSmoke = undefined;
  829. this.model = undefined;
  830. this.group = undefined;
  831. }
  832. }
  833. export default mainWindRect;