sandaoFc.threejs.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. import * as THREE from 'three';
  2. import { getTextCanvas, renderVideo } from '/@/utils/threejs/util';
  3. import gsap from 'gsap';
  4. class threeFc_8 {
  5. model;
  6. modelName = 'threedFc';
  7. group: THREE.Object3D = new THREE.Object3D();
  8. animationTimer;
  9. isLRAnimation = true;
  10. direction = 1;
  11. windowsActionArr = {
  12. frontWindow: <THREE.Mesh[]>[],
  13. centerWindow: <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. if (!this.group) {
  47. return;
  48. }
  49. const screenDownText = VENT_PARAM['modalText']
  50. ? VENT_PARAM['modalText']
  51. : History_Type['type'] == 'remote'
  52. ? `国能神东煤炭集团监制`
  53. : '煤科通安(北京)智控科技有限公司研制';
  54. const screenDownTextX = 90 - (screenDownText.length - 10) * 7;
  55. const textArr = [
  56. {
  57. text: `远程定量调节自动风窗`,
  58. font: 'normal 30px Arial',
  59. color: '#009900',
  60. strokeStyle: '#002200',
  61. x: 95,
  62. y: 97,
  63. },
  64. {
  65. text: `${selectData.OpenDegree1 ? '前窗开度值(°)' : selectData.frontArea ? '前窗过风面积(㎡)' : '前窗过风面积(㎡)'}:`,
  66. font: 'normal 28px Arial',
  67. color: '#009900',
  68. strokeStyle: '#002200',
  69. x: 5,
  70. y: 150,
  71. },
  72. {
  73. text: selectData.OpenDegree1
  74. ? Number(`${selectData.OpenDegree1}`).toFixed(2)
  75. : selectData.frontArea
  76. ? Number(`${selectData.frontArea}`).toFixed(2)
  77. : '-',
  78. font: 'normal 28px Arial',
  79. color: '#009900',
  80. strokeStyle: '#002200',
  81. x: 330,
  82. y: 150,
  83. },
  84. {
  85. text: `${selectData.OpenDegree2 ? '后窗开度值(°)' : selectData.frontArea ? '后窗过风面积(㎡)' : '后窗过风面积(㎡)'}:`,
  86. font: 'normal 28px Arial',
  87. color: '#009900',
  88. strokeStyle: '#002200',
  89. x: 5,
  90. y: 210,
  91. },
  92. {
  93. text: selectData.OpenDegree2
  94. ? Number(`${selectData.OpenDegree2}`).toFixed(2)
  95. : selectData.rearArea
  96. ? Number(`${selectData.rearArea}`).toFixed(2)
  97. : '-',
  98. font: 'normal 28px Arial',
  99. color: '#009900',
  100. strokeStyle: '#002200',
  101. x: 325,
  102. y: 210,
  103. },
  104. {
  105. text: `${selectData.frontRearDP ? '风窗压差(Pa)' : selectData.windSpeed ? '风速(m/s)' : '通信状态'}:`,
  106. font: 'normal 28px Arial',
  107. color: '#009900',
  108. strokeStyle: '#002200',
  109. x: 5,
  110. y: 266,
  111. },
  112. {
  113. text: `${
  114. selectData.frontRearDP
  115. ? selectData.frontRearDP
  116. : selectData.windSpeed
  117. ? selectData.windSpeed
  118. : selectData.netStatus == '0'
  119. ? '断开'
  120. : '连接'
  121. }`,
  122. font: 'normal 28px Arial',
  123. color: '#009900',
  124. strokeStyle: '#002200',
  125. x: 330,
  126. y: 266,
  127. },
  128. {
  129. text: screenDownText,
  130. font: 'normal 28px Arial',
  131. color: '#009900',
  132. strokeStyle: '#002200',
  133. x: screenDownTextX,
  134. y: 322,
  135. },
  136. ];
  137. getTextCanvas(570, 346, textArr, '').then((canvas: HTMLCanvasElement) => {
  138. const textMap = new THREE.CanvasTexture(canvas); // 关键一步
  139. const textMaterial = new THREE.MeshBasicMaterial({
  140. // 关于材质并未讲解 实操即可熟悉 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
  141. map: textMap, // 设置纹理贴图
  142. transparent: true,
  143. side: THREE.DoubleSide, // 这里是双面渲染的意思
  144. });
  145. textMaterial.blending = THREE.CustomBlending;
  146. const monitorPlane = this.group?.getObjectByName('monitorText');
  147. if (monitorPlane) {
  148. monitorPlane.material = textMaterial;
  149. } else {
  150. const planeGeometry = new THREE.PlaneGeometry(570, 346); // 平面3维几何体PlaneGeometry
  151. const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
  152. planeMesh.name = 'monitorText';
  153. planeMesh.scale.set(0.002, 0.002, 0.002);
  154. planeMesh.position.set(4.03, 0.658, -0.27);
  155. this.group?.add(planeMesh);
  156. }
  157. textMap.dispose();
  158. });
  159. }
  160. /* 风门动画 */
  161. render() {
  162. if (!this.model) {
  163. return;
  164. }
  165. }
  166. /* 提取风门序列帧,初始化前后门动画 */
  167. initAnimation() {
  168. const meshArr01: THREE.Object3D[] = [];
  169. const meshArr02: THREE.Object3D[] = [];
  170. const meshArr03: THREE.Object3D[] = [];
  171. const fcModal = this.group.getObjectByName('SanFengChuang_1');
  172. const fcShanyeGroup = fcModal?.getObjectByName('FengChuang');
  173. const shanye1 = fcShanyeGroup?.getObjectByName('FengChuang_1')?.getObjectByName('Men_1')?.getObjectByName('shanye_1');
  174. const shanye2 = fcShanyeGroup?.getObjectByName('FengChuang_2')?.getObjectByName('Men_2')?.getObjectByName('shanye_2');
  175. const shanye3 = fcShanyeGroup?.getObjectByName('FengChuang_3')?.getObjectByName('Men_3')?.getObjectByName('shanye_3');
  176. if (shanye1 && shanye2 && shanye3) {
  177. shanye1.children.forEach((obj) => {
  178. obj.rotateOnAxis(new THREE.Vector3(0, 0, 1), 0);
  179. meshArr01.push(obj);
  180. });
  181. shanye2.children.forEach((obj) => {
  182. obj.rotateOnAxis(new THREE.Vector3(0, 0, 1), 0);
  183. meshArr02.push(obj);
  184. });
  185. shanye3.children.forEach((obj) => {
  186. obj.rotateOnAxis(new THREE.Vector3(0, 0, 1), 0);
  187. meshArr03.push(obj);
  188. });
  189. }
  190. this.windowsActionArr.frontWindow = meshArr01;
  191. this.windowsActionArr.centerWindow = meshArr02;
  192. this.windowsActionArr.backWindow = meshArr03;
  193. }
  194. /* 点击风窗,风窗全屏 */
  195. mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
  196. if (this.animationTimer) {
  197. clearTimeout(this.animationTimer);
  198. this.animationTimer = null;
  199. }
  200. console.log('摄像头控制信息', this.model?.orbitControls, this.model?.camera);
  201. // 判断是否点击到视频
  202. intersects.find((intersect) => {
  203. const mesh = intersect.object;
  204. return false;
  205. });
  206. }
  207. mouseUpModel() {}
  208. play(rotationParam, flag) {
  209. if (this.windowsActionArr.frontWindow.length <= 0 || this.windowsActionArr.backWindow.length <= 0) {
  210. return;
  211. }
  212. if (flag === 1) {
  213. // 前风窗动画
  214. this.windowsActionArr.frontWindow.forEach((mesh) => {
  215. gsap.to(mesh.rotation, {
  216. z: -THREE.MathUtils.degToRad(rotationParam.frontDeg1),
  217. duration: (1 / 9) * Math.abs(rotationParam.frontDeg1 + mesh.rotation.z),
  218. overwrite: true,
  219. });
  220. });
  221. } else if (flag === 2) {
  222. // 后风窗动画
  223. this.windowsActionArr.backWindow.forEach((mesh) => {
  224. gsap.to(mesh.rotation, {
  225. z: -THREE.MathUtils.degToRad(rotationParam.backDeg1),
  226. duration: (1 / 9) * Math.abs(rotationParam.backDeg1 + mesh.rotation.z),
  227. overwrite: true,
  228. });
  229. });
  230. } else if (flag === 3) {
  231. // 后风窗动画
  232. this.windowsActionArr.centerWindow.forEach((mesh) => {
  233. gsap.to(mesh.rotation, {
  234. z: -THREE.MathUtils.degToRad(rotationParam.centerDeg1),
  235. duration: (1 / 9) * Math.abs(rotationParam.centerDeg1 + mesh.rotation.z),
  236. overwrite: true,
  237. });
  238. });
  239. } else if (flag === 0) {
  240. ([...this.windowsActionArr.frontWindow, ...this.windowsActionArr.backWindow, ...this.windowsActionArr.centerWindow] as THREE.Mesh[]).forEach(
  241. (mesh) => {
  242. gsap.to(mesh.rotation, {
  243. z: 0,
  244. overwrite: true,
  245. });
  246. }
  247. );
  248. }
  249. }
  250. mountedThree() {
  251. return new Promise((resolve) => {
  252. this.model.setGLTFModel('threeFc-sdg').then((gltf) => {
  253. const fcModal = gltf[0];
  254. fcModal.name = this.modelName;
  255. // this.group?.add(fcModal);
  256. this.group = fcModal;
  257. this.setModalPosition();
  258. this.initAnimation();
  259. resolve(null);
  260. this.addLight();
  261. });
  262. });
  263. }
  264. destroy() {
  265. this.model.clearGroup(this.group);
  266. this.windowsActionArr.frontWindow = undefined;
  267. this.windowsActionArr.backWindow = undefined;
  268. this.model = null;
  269. this.group = null;
  270. }
  271. }
  272. export default threeFc_8;