longmen.threejs.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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. }
  199. /* 提取风门序列帧,初始化前后门动画 */
  200. initAnimation() {
  201. const windGroup = new THREE.Group();
  202. windGroup.name = 'lmTanTou';
  203. if (this.group?.children.length) {
  204. for (let i = this.group?.children.length - 1; i > -1; i--) {
  205. const obj = this.group?.children[i];
  206. if (obj.type === 'Mesh' && obj.name && obj.name.startsWith('LMtantou')) {
  207. if (obj.name.startsWith('LMtantou')) {
  208. windGroup.add(obj.clone());
  209. this.group?.remove(obj);
  210. }
  211. }
  212. }
  213. }
  214. this.group?.add(windGroup);
  215. }
  216. /* 点击风窗,风窗全屏 */
  217. mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
  218. if (this.animationTimer) {
  219. clearTimeout(this.animationTimer);
  220. this.animationTimer = null;
  221. }
  222. // 判断是否点击到视频
  223. intersects.find((intersect) => {
  224. const mesh = intersect.object;
  225. // if (mesh.name === 'player1') {
  226. // if (new Date().getTime() - this.playerStartClickTime1 < 400) {
  227. // // 双击,视频放大
  228. // if (this.player1) {
  229. // this.player1.requestFullscreen();
  230. // }
  231. // }
  232. // this.playerStartClickTime1 = new Date().getTime();
  233. // return true;
  234. // } else if (mesh.name === 'player2') {
  235. // if (new Date().getTime() - this.playerStartClickTime2 < 400) {
  236. // // 双击,视频放大
  237. // if (this.player2) {
  238. // this.player2.requestFullscreen();
  239. // }
  240. // }
  241. // this.playerStartClickTime2 = new Date().getTime();
  242. // return true;
  243. // }
  244. return false;
  245. });
  246. }
  247. mouseUpModel() {}
  248. resetModel() {
  249. clearTimeout(this.animationTimer);
  250. }
  251. // 播放动画
  252. play(flag, isDirect = false) {
  253. const cfTanTou = this.group?.getObjectByName('lmTanTou') as THREE.Group;
  254. if (!cfTanTou) return;
  255. switch (flag) {
  256. case 'up':
  257. if (this.deviceRunState == 'up' || this.deviceRunState == 'center') {
  258. return;
  259. }
  260. if (!isDirect) {
  261. this.deviceRunState = 'up';
  262. if (this.deviceRunState == 'down') cfTanTou.position.setY(-0.48);
  263. gsap.to(cfTanTou['position'], {
  264. id: 'lm',
  265. y: 0,
  266. duration: Math.abs(cfTanTou['position']['y'] - 0) * 25,
  267. overwrite: true,
  268. });
  269. } else {
  270. cfTanTou.position.setY(0);
  271. }
  272. break;
  273. case 'center':
  274. if (this.deviceRunState) {
  275. return;
  276. }
  277. if (!isDirect) {
  278. this.deviceRunState = 'center';
  279. cfTanTou.position.setY(0);
  280. gsap.to(cfTanTou['position'], {
  281. id: 'lm',
  282. y: -0.24,
  283. duration: Math.abs(cfTanTou['position']['y'] + 0.24) * 50,
  284. overwrite: true,
  285. });
  286. } else {
  287. this.deviceRunState = 'center';
  288. cfTanTou.position.setY(-0.24);
  289. }
  290. break;
  291. case 'down':
  292. // debugger;
  293. if (this.deviceRunState == 'down' || this.deviceRunState == 'up') {
  294. return;
  295. }
  296. if (!isDirect) {
  297. if (this.deviceRunState == 'center') {
  298. cfTanTou.position.setY(-0.24);
  299. }
  300. this.deviceRunState = 'down';
  301. gsap.to(cfTanTou['position'], {
  302. id: 'lm',
  303. y: -0.48,
  304. duration: Math.abs(cfTanTou['position']['y'] + 0.48) * 50,
  305. overwrite: true,
  306. });
  307. } else {
  308. this.deviceRunState = 'down';
  309. cfTanTou.position.setY(-0.48);
  310. }
  311. break;
  312. }
  313. }
  314. async initCamera(dom1?) {
  315. const videoPlayer1 = dom1;
  316. let monitorPlane: THREE.Mesh | null = null;
  317. const canvas = await getTextCanvas(320, 180, '', 'noSinge.png');
  318. const textMap = new THREE.CanvasTexture(canvas); // 关键一步
  319. const textMaterial = new THREE.MeshBasicMaterial({
  320. map: textMap, // 设置纹理贴图
  321. transparent: true,
  322. side: THREE.DoubleSide, // 这里是双面渲染的意思
  323. });
  324. textMaterial.blending = THREE.CustomBlending;
  325. monitorPlane = this.group?.getObjectByName('noPlayer');
  326. if (monitorPlane) {
  327. monitorPlane.material = textMaterial;
  328. } else {
  329. const planeGeometry = new THREE.PlaneGeometry(100, 100); // 平面3维几何体PlaneGeometry
  330. monitorPlane = new THREE.Mesh(planeGeometry, textMaterial);
  331. textMaterial.dispose();
  332. planeGeometry.dispose();
  333. }
  334. const videoPlayer = this.group.getObjectByName('player1');
  335. if (videoPlayer) {
  336. this.model.clearMesh(videoPlayer);
  337. this.group.remove(videoPlayer);
  338. }
  339. const noPlayer1 = this.group.getObjectByName('noPlayer1');
  340. if (noPlayer1) {
  341. this.model.clearMesh(noPlayer1);
  342. this.group.remove(noPlayer1);
  343. }
  344. if (!videoPlayer1 && videoPlayer1 === null) {
  345. monitorPlane.name = 'noPlayer1';
  346. monitorPlane.scale.set(0.013, 0.007, 0.012);
  347. monitorPlane.position.set(-2.74, 0.0, -0.39);
  348. this.group?.add(monitorPlane);
  349. } else if (videoPlayer1) {
  350. const mesh = renderVideo(this.group, videoPlayer1, 'player1');
  351. if (mesh) {
  352. mesh?.scale.set(0.042, 0.036, 0.022);
  353. mesh?.position.set(-2.74, 0.03, -0.39);
  354. mesh.rotation.y = -Math.PI;
  355. this.group.add(mesh);
  356. }
  357. }
  358. }
  359. mountedThree() {
  360. return new Promise((resolve) => {
  361. this.model.setGLTFModel([this.modelName]).then((gltf) => {
  362. if (gltf[0]) {
  363. this.group = gltf[0];
  364. this.setModalPosition();
  365. this.initAnimation();
  366. this.addLight();
  367. }
  368. resolve(null);
  369. });
  370. });
  371. }
  372. destroy() {
  373. if (this.group) {
  374. this.model.clearGroup(this.group);
  375. }
  376. this.model = null;
  377. this.group = null;
  378. }
  379. }
  380. export default lmWindRect;