longmen.threejs.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. import * as THREE from 'three';
  2. import { getTextCanvas, renderVideo } from '/@/utils/threejs/util';
  3. import gsap from 'gsap';
  4. class lmWindRect {
  5. model;
  6. modelName = 'lmcf';
  7. group: THREE.Object3D = new THREE.Object3D();
  8. animationTimer;
  9. isLRAnimation = true;
  10. direction = 1;
  11. player1;
  12. player2;
  13. playerStartClickTime1 = new Date().getTime();
  14. playerStartClickTime2 = new Date().getTime();
  15. deviceRunState = '';
  16. constructor(model) {
  17. this.model = model;
  18. this.group.name = this.modelName;
  19. }
  20. addLight() {
  21. const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
  22. directionalLight.position.set(5.3, 9, 0.8);
  23. this.group.add(directionalLight);
  24. directionalLight.target = this.group;
  25. // gui.add(directionalLight.position, 'x', -10, 20).onChange(function (value) {
  26. // directionalLight.position.x = Number(value);
  27. // _this.render();
  28. // });
  29. // gui.add(directionalLight.position, 'y', -50, 50).onChange(function (value) {
  30. // directionalLight.position.y = Number(value);
  31. // _this.render();
  32. // });
  33. // gui.add(directionalLight.position, 'z', -20, 20).onChange(function (value) {
  34. // directionalLight.position.z = Number(value);
  35. // _this.render();
  36. // });
  37. const spotLight = new THREE.SpotLight();
  38. spotLight.angle = Math.PI / 16;
  39. spotLight.penumbra = 0;
  40. spotLight.castShadow = true;
  41. spotLight.distance = 0;
  42. spotLight.position.set(-470, -500, 500);
  43. this.group.add(spotLight);
  44. spotLight.shadow.camera.near = 0.5; // default
  45. spotLight.shadow.camera.far = 1000; // default
  46. spotLight.shadow.focus = 1;
  47. spotLight.shadow.bias = -0.000002;
  48. }
  49. // 设置模型位置
  50. setModalPosition() {
  51. this.group?.scale.set(22, 22, 22);
  52. this.group?.position.set(-25, 25, 15);
  53. }
  54. addMonitorText(selectData) {
  55. if (!this.group) {
  56. return;
  57. }
  58. const textArr = [
  59. {
  60. text: `龙门式测风装置`,
  61. font: 'normal 32px Arial',
  62. color: '#009900',
  63. strokeStyle: '#002200',
  64. x: 170,
  65. y: 40,
  66. },
  67. {
  68. text: `风量(m³/min):`,
  69. font: 'normal 29px Arial',
  70. color: '#009900',
  71. strokeStyle: '#002200',
  72. x: 2,
  73. y: 115,
  74. },
  75. {
  76. text: `${selectData.m3 ? selectData.m3 : '-'}`,
  77. font: 'normal 29px Arial',
  78. color: '#009900',
  79. strokeStyle: '#002200',
  80. x: 200,
  81. y: 115,
  82. },
  83. {
  84. text: `气源压力(MPa): `,
  85. font: 'normal 29px Arial',
  86. color: '#009900',
  87. strokeStyle: '#002200',
  88. x: 2,
  89. y: 182,
  90. },
  91. {
  92. text: `${selectData.temperature ? selectData.temperature : '-'}`,
  93. font: 'normal 29px Arial',
  94. color: '#009900',
  95. strokeStyle: '#002200',
  96. x: 215,
  97. y: 182,
  98. },
  99. {
  100. text: `Va(m/s):`,
  101. font: 'normal 29px Arial',
  102. color: '#009900',
  103. strokeStyle: '#002200',
  104. x: 2,
  105. y: 245,
  106. },
  107. {
  108. text: `${selectData.va ? selectData.va : '-'}`,
  109. font: 'normal 29px Arial',
  110. color: '#009900',
  111. strokeStyle: '#002200',
  112. x: 130,
  113. y: 246,
  114. },
  115. {
  116. text: `V1(m/s):`,
  117. font: 'normal 28px Arial',
  118. color: '#009900',
  119. strokeStyle: '#002200',
  120. x: 331,
  121. y: 115,
  122. },
  123. {
  124. text: `${selectData.incipientWindSpeed1 ? selectData.incipientWindSpeed1 : '-'}`,
  125. font: 'normal 28px Arial',
  126. color: '#009900',
  127. strokeStyle: '#002200',
  128. x: 455,
  129. y: 115,
  130. },
  131. {
  132. text: `V2(m/s):`,
  133. font: 'normal 28px Arial',
  134. color: '#009900',
  135. strokeStyle: '#002200',
  136. x: 330,
  137. y: 182,
  138. },
  139. {
  140. text: `${selectData.incipientWindSpeed2 ? selectData.incipientWindSpeed2 : '-'}`,
  141. font: 'normal 28px Arial',
  142. color: '#009900',
  143. strokeStyle: '#002200',
  144. x: 452,
  145. y: 182,
  146. },
  147. {
  148. text: `V3(m/s):`,
  149. font: 'normal 28px Arial',
  150. color: '#009900',
  151. strokeStyle: '#002200',
  152. x: 330,
  153. y: 245,
  154. },
  155. {
  156. text: `${selectData.incipientWindSpeed3 ? selectData.incipientWindSpeed3 : '-'}`,
  157. font: 'normal 28px Arial',
  158. color: '#009900',
  159. strokeStyle: '#002200',
  160. x: 452,
  161. y: 245,
  162. },
  163. {
  164. text: `煤炭科学技术研究院有限公司研制`,
  165. font: 'normal 28px Arial',
  166. color: '#009900',
  167. strokeStyle: '#002200',
  168. x: 60,
  169. y: 302,
  170. },
  171. ];
  172. getTextCanvas(560, 346, textArr, '').then((canvas: HTMLCanvasElement) => {
  173. const textMap = new THREE.CanvasTexture(canvas); // 关键一步
  174. const textMaterial = new THREE.MeshBasicMaterial({
  175. map: textMap, // 设置纹理贴图
  176. transparent: true,
  177. side: THREE.DoubleSide, // 这里是双面渲染的意思
  178. });
  179. textMaterial.blending = THREE.CustomBlending;
  180. const monitorPlane = this.group?.getObjectByName('monitorText');
  181. if (monitorPlane) {
  182. monitorPlane.material = textMaterial;
  183. } else {
  184. const planeGeometry = new THREE.PlaneGeometry(560, 346); // 平面3维几何体PlaneGeometry
  185. const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
  186. planeMesh.name = 'monitorText';
  187. planeMesh.scale.set(0.0022, 0.0022, 0.0022);
  188. planeMesh.position.set(3.25, -0.002, -0.41);
  189. this.group?.add(planeMesh);
  190. }
  191. });
  192. }
  193. /* 风门动画 */
  194. render() {
  195. if (!this.model) {
  196. return;
  197. }
  198. if (this.isLRAnimation && this.group) {
  199. // 左右摇摆动画
  200. if (Math.abs(this.group.rotation.y) >= 0.2) {
  201. this.direction = -this.direction;
  202. this.group.rotation.y += 0.00002 * 30 * this.direction;
  203. } else {
  204. this.group.rotation.y += 0.00002 * 30 * this.direction;
  205. }
  206. }
  207. }
  208. /* 提取风门序列帧,初始化前后门动画 */
  209. initAnimation() {
  210. const windGroup = new THREE.Group();
  211. windGroup.name = 'lmTanTou';
  212. if (this.group?.children.length) {
  213. for (let i = this.group?.children.length - 1; i > -1; i--) {
  214. const obj = this.group?.children[i];
  215. if (obj.type === 'Mesh' && obj.name && obj.name.startsWith('LMtantou')) {
  216. if (obj.name.startsWith('LMtantou')) {
  217. windGroup.add(obj.clone());
  218. this.group?.remove(obj);
  219. }
  220. }
  221. }
  222. }
  223. this.group?.add(windGroup);
  224. }
  225. /* 点击风窗,风窗全屏 */
  226. mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
  227. this.isLRAnimation = false;
  228. if (this.animationTimer) {
  229. clearTimeout(this.animationTimer);
  230. this.animationTimer = null;
  231. }
  232. // 判断是否点击到视频
  233. intersects.find((intersect) => {
  234. const mesh = intersect.object;
  235. // if (mesh.name === 'player1') {
  236. // if (new Date().getTime() - this.playerStartClickTime1 < 400) {
  237. // // 双击,视频放大
  238. // if (this.player1) {
  239. // this.player1.requestFullscreen();
  240. // }
  241. // }
  242. // this.playerStartClickTime1 = new Date().getTime();
  243. // return true;
  244. // } else if (mesh.name === 'player2') {
  245. // if (new Date().getTime() - this.playerStartClickTime2 < 400) {
  246. // // 双击,视频放大
  247. // if (this.player2) {
  248. // this.player2.requestFullscreen();
  249. // }
  250. // }
  251. // this.playerStartClickTime2 = new Date().getTime();
  252. // return true;
  253. // }
  254. return false;
  255. });
  256. }
  257. mouseUpModel() {
  258. // 10s后开始摆动
  259. if (!this.animationTimer && !this.isLRAnimation) {
  260. this.animationTimer = setTimeout(() => {
  261. this.isLRAnimation = true;
  262. }, 10000);
  263. }
  264. }
  265. resetModel() {
  266. clearTimeout(this.animationTimer);
  267. this.isLRAnimation = false;
  268. }
  269. // 播放动画
  270. play(flag, isDirect = false) {
  271. const cfTanTou = this.group?.getObjectByName('lmTanTou') as THREE.Group;
  272. if (!cfTanTou) return;
  273. switch (flag) {
  274. case 'up':
  275. if (this.deviceRunState == 'up' || this.deviceRunState == 'center') {
  276. return;
  277. }
  278. if (!isDirect) {
  279. this.deviceRunState = 'up';
  280. if (this.deviceRunState == 'down') cfTanTou.position.setY(-0.48);
  281. gsap.to(cfTanTou['position'], {
  282. id: 'lm',
  283. y: 0,
  284. duration: Math.abs(cfTanTou['position']['y'] - 0) * 25,
  285. overwrite: true,
  286. });
  287. } else {
  288. cfTanTou.position.setY(0);
  289. }
  290. break;
  291. case 'center':
  292. if (this.deviceRunState) {
  293. return;
  294. }
  295. if (!isDirect) {
  296. this.deviceRunState = 'center';
  297. cfTanTou.position.setY(0);
  298. gsap.to(cfTanTou['position'], {
  299. id: 'lm',
  300. y: -0.24,
  301. duration: Math.abs(cfTanTou['position']['y'] + 0.24) * 50,
  302. overwrite: true,
  303. });
  304. } else {
  305. this.deviceRunState = 'center';
  306. cfTanTou.position.setY(-0.24);
  307. }
  308. break;
  309. case 'down':
  310. // debugger;
  311. if (this.deviceRunState == 'down' || this.deviceRunState == 'up') {
  312. return;
  313. }
  314. if (!isDirect) {
  315. if (this.deviceRunState == 'center') {
  316. cfTanTou.position.setY(-0.24);
  317. }
  318. this.deviceRunState = 'down';
  319. gsap.to(cfTanTou['position'], {
  320. id: 'lm',
  321. y: -0.48,
  322. duration: Math.abs(cfTanTou['position']['y'] + 0.48) * 50,
  323. overwrite: true,
  324. });
  325. } else {
  326. this.deviceRunState = 'down';
  327. cfTanTou.position.setY(-0.48);
  328. }
  329. break;
  330. }
  331. }
  332. async initCamera(dom1?) {
  333. const videoPlayer1 = dom1;
  334. let monitorPlane: THREE.Mesh | null = null;
  335. const canvas = await getTextCanvas(320, 180, '', 'noSinge.png');
  336. const textMap = new THREE.CanvasTexture(canvas); // 关键一步
  337. const textMaterial = new THREE.MeshBasicMaterial({
  338. map: textMap, // 设置纹理贴图
  339. transparent: true,
  340. side: THREE.DoubleSide, // 这里是双面渲染的意思
  341. });
  342. textMaterial.blending = THREE.CustomBlending;
  343. monitorPlane = this.group?.getObjectByName('noPlayer');
  344. if (monitorPlane) {
  345. monitorPlane.material = textMaterial;
  346. } else {
  347. const planeGeometry = new THREE.PlaneGeometry(100, 100); // 平面3维几何体PlaneGeometry
  348. monitorPlane = new THREE.Mesh(planeGeometry, textMaterial);
  349. textMaterial.dispose();
  350. planeGeometry.dispose();
  351. }
  352. const videoPlayer = this.group.getObjectByName('player1');
  353. if (videoPlayer) {
  354. this.model.clearMesh(videoPlayer);
  355. this.group.remove(videoPlayer);
  356. }
  357. const noPlayer1 = this.group.getObjectByName('noPlayer1');
  358. if (noPlayer1) {
  359. this.model.clearMesh(noPlayer1);
  360. this.group.remove(noPlayer1);
  361. }
  362. if (!videoPlayer1 && videoPlayer1 === null) {
  363. monitorPlane.name = 'noPlayer1';
  364. monitorPlane.scale.set(0.013, 0.007, 0.012);
  365. monitorPlane.position.set(-2.74, 0.0, -0.39);
  366. this.group?.add(monitorPlane);
  367. } else if (videoPlayer1) {
  368. const mesh = renderVideo(this.group, videoPlayer1, 'player1');
  369. if (mesh) {
  370. mesh?.scale.set(0.042, 0.036, 0.022);
  371. mesh?.position.set(-2.74, 0.03, -0.39);
  372. mesh.rotation.y = -Math.PI;
  373. this.group.add(mesh);
  374. }
  375. }
  376. }
  377. mountedThree() {
  378. return new Promise((resolve) => {
  379. this.model.setGLTFModel([this.modelName]).then((gltf) => {
  380. if (gltf[0]) {
  381. this.group = gltf[0];
  382. this.setModalPosition();
  383. this.initAnimation();
  384. this.addLight();
  385. }
  386. resolve(null);
  387. });
  388. });
  389. }
  390. destroy() {
  391. if (this.group) {
  392. this.model.clearGroup(this.group);
  393. }
  394. this.model = null;
  395. this.group = null;
  396. }
  397. }
  398. export default lmWindRect;