shuangdaoFc.threejs.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. import * as THREE from 'three';
  2. import { getTextCanvas, renderVideo } from '/@/utils/threejs/util';
  3. import gsap from 'gsap';
  4. import { useGlobSetting } from '/@/hooks/setting';
  5. class sdFc_1 {
  6. model;
  7. modelName = 'sdFc';
  8. group: THREE.Object3D = new THREE.Object3D();
  9. animationTimer;
  10. isLRAnimation = true;
  11. direction = 1;
  12. windowsActionArr = {
  13. frontWindow: <THREE.Mesh[]>[],
  14. backWindow: <THREE.Mesh[]>[],
  15. };
  16. constructor(model) {
  17. this.model = model;
  18. // this.group.name = this.modelName;
  19. }
  20. addLight = () => {
  21. if (!this.model) return;
  22. const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
  23. directionalLight.position.set(-110, 150, 647);
  24. this.group?.add(directionalLight);
  25. // directionalLight.target = group;
  26. const pointLight2 = new THREE.PointLight(0xffffff, 1, 150);
  27. pointLight2.position.set(-101, 34, 16);
  28. pointLight2.shadow.bias = 0.05;
  29. this.group.add(pointLight2);
  30. const pointLight3 = new THREE.PointLight(0xffffff, 1, 150);
  31. pointLight3.position.set(19, 25, -7);
  32. pointLight3.shadow.bias = 0.05;
  33. this.group.add(pointLight3);
  34. const pointLight6 = new THREE.PointLight(0xffffff, 1, 300);
  35. pointLight6.position.set(51, 51, 9);
  36. pointLight6.shadow.bias = 0.05;
  37. this.group.add(pointLight6);
  38. };
  39. // 设置模型位置
  40. setModalPosition() {
  41. // this.group.getObjectByName(this.modelName)?.scale.set(9, 9, 9);
  42. this.group?.scale.set(22, 22, 22);
  43. this.group?.position.set(-15, 25, 15);
  44. }
  45. addMonitorText(selectData) {
  46. const { sysOrgCode } = useGlobSetting();
  47. if (!this.group) {
  48. return;
  49. }
  50. const screenDownText = VENT_PARAM['modalText']
  51. ? VENT_PARAM['modalText']
  52. : History_Type['type'] == 'remote'
  53. ? `国能神东煤炭集团监制`
  54. : '煤炭科学技术研究院有限公司研制';
  55. const screenDownTextX = 120 - (screenDownText.length - 10) * 6;
  56. const textArr = [
  57. {
  58. text: `${sysOrgCode == 'zlnxnywwek' ? selectData.strinstallpos : '远程控制自动风门'}`, //王洼二矿需要特殊处理
  59. font: 'normal 30px Arial',
  60. color: '#009900',
  61. strokeStyle: '#002200',
  62. x: sysOrgCode == 'zlnxnywwek' ? (450 - selectData.strinstallpos.length * 22) / 2 : 120,
  63. y: 97,
  64. },
  65. {
  66. text: `${selectData.OpenDegree1 ? '前窗开度值(°)' : selectData.forntArea ? '前窗过风面积(㎡)' : '前窗过风面积(㎡)'}:`,
  67. font: 'normal 28px Arial',
  68. color: '#009900',
  69. strokeStyle: '#002200',
  70. x: 5,
  71. y: 150,
  72. },
  73. {
  74. text: selectData.OpenDegree1
  75. ? Number(`${selectData.OpenDegree1}`).toFixed(2)
  76. : selectData.forntArea
  77. ? Number(`${selectData.forntArea}`).toFixed(2)
  78. : '-',
  79. font: 'normal 28px Arial',
  80. color: '#009900',
  81. strokeStyle: '#002200',
  82. x: 330,
  83. y: 150,
  84. },
  85. {
  86. text: `${selectData.OpenDegree2 ? '后窗开度值(°)' : selectData.forntArea ? '后窗过风面积(㎡)' : '后窗过风面积(㎡)'}:`,
  87. font: 'normal 28px Arial',
  88. color: '#009900',
  89. strokeStyle: '#002200',
  90. x: 5,
  91. y: 210,
  92. },
  93. {
  94. text: selectData.OpenDegree2
  95. ? Number(`${selectData.OpenDegree2}`).toFixed(2)
  96. : selectData.rearArea
  97. ? Number(`${selectData.rearArea}`).toFixed(2)
  98. : '-',
  99. font: 'normal 28px Arial',
  100. color: '#009900',
  101. strokeStyle: '#002200',
  102. x: 325,
  103. y: 210,
  104. },
  105. {
  106. text: `${selectData.frontRearDP ? '风窗压差(Pa)' : selectData.windSpeed ? '风速(m/s)' : '通信状态'}:`,
  107. font: 'normal 28px Arial',
  108. color: '#009900',
  109. strokeStyle: '#002200',
  110. x: 5,
  111. y: 266,
  112. },
  113. {
  114. text: `${
  115. selectData.frontRearDP
  116. ? selectData.frontRearDP
  117. : selectData.windSpeed
  118. ? selectData.windSpeed
  119. : selectData.netStatus == '0'
  120. ? '断开'
  121. : '连接'
  122. }`,
  123. font: 'normal 28px Arial',
  124. color: '#009900',
  125. strokeStyle: '#002200',
  126. x: 330,
  127. y: 266,
  128. },
  129. {
  130. text: screenDownText,
  131. font: 'normal 28px Arial',
  132. color: '#009900',
  133. strokeStyle: '#002200',
  134. x: screenDownTextX,
  135. y: 322,
  136. },
  137. ];
  138. getTextCanvas(570, 346, textArr, '').then((canvas: HTMLCanvasElement) => {
  139. const textMap = new THREE.CanvasTexture(canvas); // 关键一步
  140. const textMaterial = new THREE.MeshBasicMaterial({
  141. // 关于材质并未讲解 实操即可熟悉 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
  142. map: textMap, // 设置纹理贴图
  143. transparent: true,
  144. side: THREE.DoubleSide, // 这里是双面渲染的意思
  145. });
  146. textMaterial.blending = THREE.CustomBlending;
  147. const monitorPlane = this.group?.getObjectByName('monitorText');
  148. if (monitorPlane) {
  149. monitorPlane.material = textMaterial;
  150. } else {
  151. const planeGeometry = new THREE.PlaneGeometry(570, 346); // 平面3维几何体PlaneGeometry
  152. const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
  153. planeMesh.name = 'monitorText';
  154. planeMesh.scale.set(0.0018, 0.0018, 0.0018);
  155. planeMesh.position.set(4.04, 0.178, -0.2);
  156. this.group?.add(planeMesh);
  157. }
  158. textMap.dispose();
  159. });
  160. }
  161. /* 风门动画 */
  162. render() {
  163. if (!this.model) {
  164. return;
  165. }
  166. }
  167. /* 提取风门序列帧,初始化前后门动画 */
  168. initAnimation() {
  169. const meshArr01: THREE.Object3D[] = [];
  170. const meshArr02: THREE.Object3D[] = [];
  171. const windowGroup = new THREE.Group();
  172. windowGroup.name = 'hiddenGroup';
  173. this.group.getObjectByName('sdFc')?.children.forEach((obj) => {
  174. if (obj.type === 'Mesh' && obj.name && (obj.name.startsWith('shanye') || obj.name.startsWith('FCshanye'))) {
  175. if (obj.name.startsWith('FCshanye')) {
  176. obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
  177. meshArr01.push(obj);
  178. } else if (obj.name.startsWith('shanye')) {
  179. obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
  180. meshArr02.push(obj);
  181. }
  182. }
  183. });
  184. this.windowsActionArr.frontWindow = meshArr01;
  185. this.windowsActionArr.backWindow = meshArr02;
  186. this.group?.add(windowGroup);
  187. }
  188. /* 点击风窗,风窗全屏 */
  189. mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
  190. if (this.animationTimer) {
  191. clearTimeout(this.animationTimer);
  192. this.animationTimer = null;
  193. }
  194. // 判断是否点击到视频
  195. intersects.find((intersect) => {
  196. const mesh = intersect.object;
  197. return false;
  198. });
  199. }
  200. mouseUpModel() {}
  201. play(rotationParam, flag) {
  202. if (this.windowsActionArr.frontWindow.length <= 0 || this.windowsActionArr.backWindow.length <= 0) {
  203. return;
  204. }
  205. if (flag === 1) {
  206. // 前风窗动画
  207. this.windowsActionArr.frontWindow.forEach((mesh) => {
  208. gsap.to(mesh.rotation, {
  209. y: THREE.MathUtils.degToRad(rotationParam.frontDeg1),
  210. duration: (1 / 9) * Math.abs(rotationParam.frontDeg1 - mesh.rotation.y),
  211. overwrite: true,
  212. });
  213. });
  214. } else if (flag === 2) {
  215. // 后风窗动画
  216. this.windowsActionArr.backWindow.forEach((mesh) => {
  217. gsap.to(mesh.rotation, {
  218. y: THREE.MathUtils.degToRad(rotationParam.backDeg1),
  219. duration: (1 / 9) * Math.abs(rotationParam.backDeg1 - mesh.rotation.y),
  220. overwrite: true,
  221. });
  222. });
  223. } else if (flag === 0) {
  224. ([...this.windowsActionArr.frontWindow, ...this.windowsActionArr.backWindow] as THREE.Mesh[]).forEach((mesh) => {
  225. gsap.to(mesh.rotation, {
  226. y: 0,
  227. overwrite: true,
  228. });
  229. });
  230. }
  231. }
  232. mountedThree() {
  233. return new Promise((resolve) => {
  234. this.model.setGLTFModel(['sdFc'], this.group).then(() => {
  235. this.setModalPosition();
  236. this.initAnimation();
  237. resolve(null);
  238. this.addLight();
  239. // this.initCamera(playerDom);
  240. });
  241. });
  242. }
  243. destroy() {
  244. this.model.clearGroup(this.group);
  245. this.windowsActionArr.frontWindow = undefined;
  246. this.windowsActionArr.backWindow = undefined;
  247. this.model = null;
  248. this.group = null;
  249. }
  250. }
  251. export default sdFc_1;