longmen.threejs.ts 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  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.Group | null = null;
  8. animationTimer;
  9. isLRAnimation = true;
  10. direction = 1;
  11. player1;
  12. player2;
  13. playerStartClickTime1 = new Date().getTime();
  14. playerStartClickTime2 = new Date().getTime();
  15. constructor(model, playerVal1, playerVal2) {
  16. this.model = model;
  17. this.player1 = playerVal1;
  18. this.player2 = playerVal2;
  19. }
  20. addLight() {
  21. const pointLight2 = new THREE.PointLight(0xffeeee, 1, 0);
  22. pointLight2.position.set(317, 41, -21);
  23. pointLight2.shadow.bias = 0.05;
  24. this.model.add(pointLight2);
  25. //
  26. const pointLight3 = new THREE.PointLight(0xffffff, 1, 138);
  27. pointLight3.position.set(-202, 50, 27.8);
  28. pointLight3.shadow.bias = 0.05;
  29. this.model.add(pointLight3);
  30. //
  31. const pointLight4 = new THREE.PointLight(0xffeeee, 1, 162);
  32. pointLight4.position.set(83, 62, -14);
  33. pointLight4.shadow.bias = 0.05;
  34. this.model.add(pointLight4);
  35. //
  36. const pointLight5 = new THREE.PointLight(0xffffff, 0.8, 176);
  37. pointLight5.position.set(-123, 61.6, -18);
  38. pointLight5.shadow.bias = 0.05;
  39. this.model.add(pointLight5);
  40. //
  41. const pointLight6 = new THREE.PointLight(0xffffff, 1.5, 64);
  42. pointLight6.position.set(-76, 48, 18);
  43. pointLight6.shadow.bias = 0.05;
  44. this.model.add(pointLight6);
  45. const pointLight7 = new THREE.PointLight(0xffffff, 1, 302);
  46. pointLight7.position.set(-220, -177, -3.5);
  47. pointLight7.shadow.bias = -0.05;
  48. this.model.add(pointLight7);
  49. const spotLight = new THREE.SpotLight();
  50. spotLight.angle = Math.PI / 16;
  51. spotLight.penumbra = 0;
  52. spotLight.castShadow = true;
  53. spotLight.distance = 0;
  54. spotLight.position.set(-470, -500, 500);
  55. this.model.add(spotLight);
  56. spotLight.shadow.camera.near = 0.5; // default
  57. spotLight.shadow.camera.far = 1000; // default
  58. spotLight.shadow.focus = 1;
  59. spotLight.shadow.bias = -0.000002;
  60. }
  61. // 设置模型位置
  62. setModalPosition() {
  63. this.group?.scale.set(22, 22, 22);
  64. this.group?.position.set(-25, 25, 15);
  65. }
  66. addFmText(selectData) {
  67. if (!this.group) {
  68. return;
  69. }
  70. const textArr = [
  71. {
  72. text: `煤矿巷道远程风窗系统`,
  73. font: 'normal 2.2rem Arial',
  74. color: '#009900',
  75. strokeStyle: '#002200',
  76. x: 90,
  77. y: 95,
  78. },
  79. {
  80. text: `过风量(m3/min):`,
  81. font: 'normal 30px Arial',
  82. color: '#009900',
  83. strokeStyle: '#002200',
  84. x: 5,
  85. y: 115,
  86. },
  87. {
  88. text: `${
  89. selectData.frontRearDifference && selectData.rearPresentValue
  90. ? Math.min(selectData.frontRearDifference, selectData.rearPresentValue)
  91. : selectData.frontRearDifference || selectData.rearPresentValue || '-'
  92. }`,
  93. font: 'normal 30px Arial',
  94. color: '#009900',
  95. strokeStyle: '#002200',
  96. x: 235,
  97. y: 115,
  98. },
  99. {
  100. text: `过风面积(m2): `,
  101. font: 'normal 30px Arial',
  102. color: '#009900',
  103. strokeStyle: '#002200',
  104. x: 5,
  105. y: 182,
  106. },
  107. {
  108. text: `${
  109. selectData.forntArea && selectData.rearArea
  110. ? Math.min(selectData.forntArea, selectData.rearArea)
  111. : selectData.forntArea || selectData.rearArea || '-'
  112. }`,
  113. font: 'normal 30px Arial',
  114. color: '#009900',
  115. strokeStyle: '#002200',
  116. x: 200,
  117. y: 182,
  118. },
  119. {
  120. text: `风窗压差(Pa):`,
  121. font: 'normal 30px Arial',
  122. color: '#009900',
  123. strokeStyle: '#002200',
  124. x: 5,
  125. y: 245,
  126. },
  127. {
  128. text: `${selectData.dataDh}`,
  129. font: 'normal 30px Arial',
  130. color: '#009900',
  131. strokeStyle: '#002200',
  132. x: 200,
  133. y: 245,
  134. },
  135. {
  136. text: `调节精度:`,
  137. font: 'normal 30px Arial',
  138. color: '#009900',
  139. strokeStyle: '#002200',
  140. x: 320,
  141. y: 115,
  142. },
  143. {
  144. text: `1% FS`,
  145. font: 'normal 30px Arial',
  146. color: '#009900',
  147. strokeStyle: '#002200',
  148. x: 460,
  149. y: 115,
  150. },
  151. {
  152. text: `调节范围:`,
  153. font: 'normal 30px Arial',
  154. color: '#009900',
  155. strokeStyle: '#002200',
  156. x: 320,
  157. y: 182,
  158. },
  159. {
  160. text: `${selectData.maxarea}`,
  161. font: 'normal 30px Arial',
  162. color: '#009900',
  163. strokeStyle: '#002200',
  164. x: 460,
  165. y: 182,
  166. },
  167. {
  168. text: `煤炭科学技术研究院有限公司研制`,
  169. font: 'normal 28px Arial',
  170. color: '#009900',
  171. strokeStyle: '#002200',
  172. x: 60,
  173. y: 302,
  174. },
  175. ];
  176. getTextCanvas(560, 346, textArr, '').then((canvas: HTMLCanvasElement) => {
  177. const textMap = new THREE.CanvasTexture(canvas); // 关键一步
  178. const textMaterial = new THREE.MeshBasicMaterial({
  179. map: textMap, // 设置纹理贴图
  180. transparent: true,
  181. side: THREE.DoubleSide, // 这里是双面渲染的意思
  182. });
  183. textMaterial.blending = THREE.CustomBlending;
  184. const monitorPlane = this.group?.getObjectByName('monitorText');
  185. if (monitorPlane) {
  186. monitorPlane.material = textMaterial;
  187. } else {
  188. const planeGeometry = new THREE.PlaneGeometry(560, 346); // 平面3维几何体PlaneGeometry
  189. const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
  190. planeMesh.name = 'monitorText';
  191. planeMesh.scale.set(0.0022, 0.0022, 0.0022);
  192. planeMesh.position.set(3.25, -0.002, -0.41);
  193. this.group?.add(planeMesh);
  194. }
  195. });
  196. }
  197. /* 风门动画 */
  198. render() {
  199. if (!this.model) {
  200. return;
  201. }
  202. if (this.isLRAnimation && this.group) {
  203. // 左右摇摆动画
  204. if (Math.abs(this.group.rotation.y) >= 0.2) {
  205. this.direction = -this.direction;
  206. this.group.rotation.y += 0.00005 * 30 * this.direction;
  207. } else {
  208. this.group.rotation.y += 0.00005 * 30 * this.direction;
  209. }
  210. }
  211. }
  212. /* 提取风门序列帧,初始化前后门动画 */
  213. initAnimation() {
  214. const windGroup = new THREE.Group();
  215. windGroup.name = 'lmTanTou';
  216. this.group?.children.forEach((obj) => {
  217. if (obj.type === 'Mesh' && obj.name && obj.name.startsWith('LMtantou')) {
  218. if (obj.name.startsWith('LMtantou')) {
  219. windGroup.add(obj.clone());
  220. this.group?.remove(obj);
  221. }
  222. }
  223. });
  224. this.group?.add(windGroup);
  225. }
  226. /* 点击风窗,风窗全屏 */
  227. mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
  228. this.isLRAnimation = false;
  229. if (this.animationTimer) {
  230. clearTimeout(this.animationTimer);
  231. this.animationTimer = null;
  232. }
  233. // 判断是否点击到视频
  234. intersects.find((intersect) => {
  235. const mesh = intersect.object;
  236. if (mesh.name === 'player1') {
  237. if (new Date().getTime() - this.playerStartClickTime1 < 400) {
  238. // 双击,视频放大
  239. if (this.player1) {
  240. this.player1.requestFullscreen();
  241. }
  242. }
  243. this.playerStartClickTime1 = new Date().getTime();
  244. return true;
  245. } else if (mesh.name === 'player2') {
  246. if (new Date().getTime() - this.playerStartClickTime2 < 400) {
  247. // 双击,视频放大
  248. if (this.player2) {
  249. this.player2.requestFullscreen();
  250. }
  251. }
  252. this.playerStartClickTime2 = new Date().getTime();
  253. return true;
  254. }
  255. return false;
  256. });
  257. }
  258. mouseUpModel() {
  259. // 10s后开始摆动
  260. if (!this.animationTimer && !this.isLRAnimation) {
  261. this.animationTimer = setTimeout(() => {
  262. this.isLRAnimation = true;
  263. }, 10000);
  264. }
  265. }
  266. resetModel() {
  267. clearTimeout(this.animationTimer);
  268. this.isLRAnimation = false;
  269. }
  270. // 播放动画
  271. play(flag) {
  272. const cfTanTou = this.group?.getObjectByName('lmTanTou') as THREE.Group;
  273. if (!cfTanTou) return;
  274. switch (flag) {
  275. case 'up':
  276. gsap.to(cfTanTou['position'], {
  277. y: 0,
  278. duration: Math.abs(cfTanTou['position']['y'] - 0) / 14,
  279. ease: 'easeQutQuad',
  280. overwrite: true,
  281. });
  282. break;
  283. case 'center':
  284. gsap.to(cfTanTou['position'], {
  285. y: -7,
  286. duration: Math.abs(cfTanTou['position']['y'] + 7) / 14,
  287. ease: 'easeQutQuad',
  288. overwrite: true,
  289. });
  290. break;
  291. case 'down':
  292. gsap.to(cfTanTou['position'], {
  293. y: -14,
  294. duration: Math.abs(cfTanTou['position']['y'] + 14) / 14,
  295. ease: 'easeQutCubic',
  296. overwrite: true,
  297. });
  298. break;
  299. }
  300. }
  301. mountedThree() {
  302. return new Promise((resolve) => {
  303. this.model.setModel(this.modelName).then((gltf) => {
  304. this.group = gltf.scene;
  305. this.setModalPosition();
  306. this.initAnimation();
  307. setTimeout(async () => {
  308. const videoPlayer1 = document.getElementById('cf-player1')?.getElementsByClassName('vjs-tech')[0];
  309. // const videoPlayer2 = document.getElementById('cf-player2')?.getElementsByClassName('vjs-tech')[0];
  310. if (videoPlayer1) {
  311. const mesh = renderVideo(this.group, videoPlayer1, 'player1');
  312. mesh?.scale.set(0.042, 0.036, 0.022);
  313. mesh?.position.set(-2.74, 0.03, -0.39);
  314. this.group?.add(mesh as THREE.Mesh);
  315. }
  316. // if (videoPlayer2) {
  317. // const mesh = renderVideo(this.group, videoPlayer2, 'player2');
  318. // mesh?.scale.set(0.0385, 0.028, 0.022);
  319. // mesh?.position.set(-86.77, 0.405, -9.62);
  320. // this.group?.add(mesh as THREE.Mesh);
  321. // }
  322. resolve(null);
  323. }, 0);
  324. });
  325. });
  326. }
  327. destroy() {
  328. if (this.group) {
  329. this.model.clearGroup(this.group);
  330. }
  331. this.model = null;
  332. this.group = null;
  333. }
  334. }
  335. export default lmWindRect;