fanLocalDual.threejs.base.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. import * as THREE from 'three';
  2. // import { setModalCenter } from '/@/utils/threejs/util';
  3. import Smoke from '../../comment/threejs/Smoke';
  4. import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer';
  5. // import * as dat from 'dat.gui';
  6. // const gui = new dat.GUI();
  7. // gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
  8. class ModelContext {
  9. model;
  10. // modelName = 'jbfj-hd';
  11. modelName = 'jbfj-dual';
  12. /** 本模型的根3D对象 */
  13. group?: THREE.Object3D;
  14. /** 本模型所包含的所有元素合集 */
  15. private elements: unknown[] = [];
  16. /** 本模型支持的 Object3DGroup 模块 */
  17. private modules: {
  18. /** 模块名称 */
  19. name: string;
  20. /** 控制该模块所用的上下文 */
  21. context: THREE.Object3D;
  22. /** 控制时行为声明 */
  23. behavior: (context: THREE.Object3D) => void;
  24. }[] = [];
  25. constructor(model) {
  26. this.model = model;
  27. }
  28. addLight() {
  29. // optional implementation
  30. }
  31. /** 设置模型类型并切换,不同的类型通常对应不同的具体模型,在模型总控制器下的具体模型会根据传入的参数彼此交互、切换 */
  32. setModelType(modelType: string) {
  33. this.modules.forEach(({ name, context, behavior }) => {
  34. if (name === modelType) {
  35. behavior(context);
  36. }
  37. });
  38. }
  39. /** 初始化css元素,将css元素选择器传入,该方法会将这些元素按顺序放入传入的锚点中 */
  40. initCssElement() {
  41. // selectors.forEach((selector, index) => {
  42. // const element = document.querySelector(selector) as HTMLElement;
  43. // if (element) {
  44. // const css3D = new CSS3DSprite(element);
  45. // this.cssSprites.push(css3D);
  46. // css3D.name = selector;
  47. // css3D.scale.set(0.05, 0.05, 0.05);
  48. // // const ff = gui.addFolder(`css元素${index}`);
  49. // // ff.add(css3D.position, 'x', -100, 100);
  50. // // ff.add(css3D.position, 'y', -100, 100);
  51. // // ff.add(css3D.position, 'z', -100, 100);
  52. // if (index < anchors.length) {
  53. // const [x, y, z] = anchors[index];
  54. // css3D.position.set(x, y, z);
  55. // this.group?.add(css3D);
  56. // } else {
  57. // console.warn(`指定的元素${selector}没有合适的位置放置`);
  58. // }
  59. // }
  60. // });
  61. }
  62. mountedThree() {
  63. return new Promise((resolve) => {
  64. this.model.setGLTFModel([this.modelName]).then(async (gltf) => {
  65. this.group = gltf[0];
  66. if (this.group) {
  67. // setModalCenter(this.group);
  68. this.addLight();
  69. this.setModelPosition();
  70. this.initModules().then(resolve);
  71. }
  72. });
  73. });
  74. }
  75. destroy() {
  76. if (!this.model) return;
  77. this.elements.forEach((element) => {
  78. this.model.clearGroup(element);
  79. });
  80. }
  81. // 设置模型位置
  82. setModelPosition() {
  83. if (!this.group) return;
  84. this.group.scale.set(0.6, 0.6, 0.6);
  85. // const ff = gui.addFolder(`位置调整`);
  86. // ff.add(this.group.position, 'x', -100, 100);
  87. // ff.add(this.group.position, 'y', -100, 100);
  88. // ff.add(this.group.position, 'z', -100, 100);
  89. this.group.position.set(0, 0, -60);
  90. this.group.rotation.y = Math.PI / 2;
  91. }
  92. // hideElements(eles: THREE.Object3D[]) {
  93. // eles.forEach((g) => {
  94. // g.visible = false;
  95. // });
  96. // }
  97. // showElements(eles: THREE.Object3D[]) {
  98. // eles.forEach((g) => {
  99. // g.visible = true;
  100. // });
  101. // }
  102. weakElements(eles: unknown[]) {
  103. eles.forEach((g) => {
  104. if (g instanceof Smoke) {
  105. g.oldOpacityFactor = 0.4;
  106. }
  107. if (g instanceof CSS3DObject) {
  108. g.element.style.setProperty('opacity', '0.5');
  109. }
  110. });
  111. }
  112. strongElements(eles: unknown[]) {
  113. eles.forEach((g) => {
  114. if (g instanceof Smoke) {
  115. g.oldOpacityFactor = 0.75;
  116. }
  117. if (g instanceof CSS3DObject) {
  118. g.element.style.setProperty('opacity', '1');
  119. }
  120. });
  121. }
  122. startAnimation(eles: unknown[]) {
  123. eles.forEach((g) => {
  124. if (g instanceof Smoke) {
  125. g.startSmoke();
  126. }
  127. });
  128. }
  129. stopAnimation(eles: unknown[]) {
  130. eles.forEach((g) => {
  131. if (g instanceof Smoke) {
  132. g.stopSmoke();
  133. }
  134. });
  135. }
  136. /** 核心方法,初始化本模型的各个模块,这些模块可以实现特定场景的展示、控制等功能 */
  137. async initModules() {
  138. if (this.elements.length > 0) return;
  139. // 右侧风机-主风机进风
  140. const curveFan1Right = this.generateSmokePath(
  141. [
  142. new THREE.Vector3(-85.685, 4.208, 43.895),
  143. new THREE.Vector3(-85.685, 2.208, 41.895),
  144. new THREE.Vector3(-85.685, 2.188, 35.327),
  145. new THREE.Vector3(-85.685, 0.784, 33.086),
  146. new THREE.Vector3(-85.685, 0.784, 27.848),
  147. new THREE.Vector3(-85.685, 4.724, 21.565),
  148. new THREE.Vector3(-85.685, 4.724, -12.993),
  149. new THREE.Vector3(-26.191, 4.724, -13.232),
  150. new THREE.Vector3(-25.608, 4.724, -47.022),
  151. new THREE.Vector3(80.038, 4.724, -47.022),
  152. ],
  153. true
  154. );
  155. // 右侧风机-备风机进风
  156. const curveFan2Right = this.generateSmokePath(
  157. [
  158. new THREE.Vector3(-85.685, 1.475, 43.895),
  159. new THREE.Vector3(-85.685, -0.525, 41.895),
  160. new THREE.Vector3(-85.685, -0.525, 35.327),
  161. new THREE.Vector3(-85.685, 0.784, 33.086),
  162. new THREE.Vector3(-85.685, 0.784, 27.848),
  163. new THREE.Vector3(-85.685, 4.724, 21.565),
  164. new THREE.Vector3(-85.685, 4.724, -12.993),
  165. new THREE.Vector3(-26.191, 4.724, -13.232),
  166. new THREE.Vector3(-25.608, 4.724, -47.022),
  167. new THREE.Vector3(80.038, 4.724, -47.022),
  168. ],
  169. true
  170. );
  171. // 左侧风机-主风机进风
  172. const curveFan1Left = this.generateSmokePath(
  173. [
  174. new THREE.Vector3(-85.685, 4.188, 4.729),
  175. new THREE.Vector3(-85.685, 2.188, 2.729),
  176. new THREE.Vector3(-85.685, 2.188, -3.84),
  177. new THREE.Vector3(-85.685, 0.784, -6.081),
  178. new THREE.Vector3(-85.685, 0.784, -12.912),
  179. new THREE.Vector3(80.251, 0.784, -12.912),
  180. ],
  181. true
  182. );
  183. // 左侧风机-备风机进风
  184. const curveFan2Left = this.generateSmokePath(
  185. [
  186. new THREE.Vector3(-85.685, 1.475, 4.729),
  187. new THREE.Vector3(-85.685, -0.525, 2.729),
  188. new THREE.Vector3(-85.685, -0.508, -3.84),
  189. new THREE.Vector3(-85.685, 0.784, -6.081),
  190. new THREE.Vector3(-85.685, 0.784, -12.912),
  191. new THREE.Vector3(80.251, 0.784, -12.912),
  192. ],
  193. true
  194. );
  195. const group1 = new THREE.Group();
  196. const smokeFan1Right = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.75, 0.5, 400);
  197. smokeFan1Right.setPath(curveFan1Right);
  198. this.elements.push(smokeFan1Right);
  199. const smokeFan2Right = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.75, 0.5, 400);
  200. smokeFan2Right.setPath(curveFan2Right);
  201. this.elements.push(smokeFan2Right);
  202. const smokeFan1Left = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.75, 0.5, 400);
  203. smokeFan1Left.setPath(curveFan1Left);
  204. this.elements.push(smokeFan1Left);
  205. const smokeFan2Left = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.75, 0.5, 400);
  206. smokeFan2Left.setPath(curveFan2Left);
  207. this.elements.push(smokeFan2Left);
  208. await smokeFan1Right.setPoints();
  209. this.group?.add(smokeFan1Right.points);
  210. await smokeFan2Right.setPoints();
  211. this.group?.add(smokeFan2Right.points);
  212. await smokeFan1Left.setPoints();
  213. this.group?.add(smokeFan1Left.points);
  214. await smokeFan2Left.setPoints();
  215. this.group?.add(smokeFan2Left.points);
  216. // const element = document.getElementById('inputBox') as HTMLElement;
  217. // if (element) {
  218. // const fanLocalCSS3D = new CSS3DObject(element);
  219. // fanLocalCSS3D.name = 'text1';
  220. // fanLocalCSS3D.scale.set(0.04, 0.04, 0.04);
  221. // fanLocalCSS3D.rotation.y = -Math.PI / 2;
  222. // fanLocalCSS3D.position.set(-85.68, 5.97, -3.39);
  223. // group1.add(fanLocalCSS3D);
  224. // this.elements.push(fanLocalCSS3D);
  225. // }
  226. this.modules.push({
  227. name: 'fan1RightOpen',
  228. context: group1,
  229. behavior: () => {
  230. this.weakElements(this.elements);
  231. this.startAnimation([smokeFan1Right]);
  232. this.strongElements([smokeFan1Right]);
  233. },
  234. });
  235. this.modules.push({
  236. name: 'fan2RightOpen',
  237. context: group1,
  238. behavior: () => {
  239. this.weakElements(this.elements);
  240. this.startAnimation([smokeFan2Right]);
  241. this.strongElements([smokeFan2Right]);
  242. },
  243. });
  244. this.modules.push({
  245. name: 'fan1LeftOpen',
  246. context: group1,
  247. behavior: () => {
  248. this.weakElements(this.elements);
  249. this.startAnimation([smokeFan1Left]);
  250. this.strongElements([smokeFan1Left]);
  251. },
  252. });
  253. this.modules.push({
  254. name: 'fan2LeftOpen',
  255. context: group1,
  256. behavior: () => {
  257. this.weakElements(this.elements);
  258. this.startAnimation([smokeFan2Left]);
  259. this.strongElements([smokeFan2Left]);
  260. },
  261. });
  262. }
  263. /** 生成适用于 Smoke 的曲线数据,输入途径点,输出路径,如果是进风类型,首个线段将有扩散效果,出风则是末尾线段有扩散效果 */
  264. generateSmokePath(points: THREE.Vector3[], airIn?: boolean, airOut?: boolean) {
  265. const result: any[] = [];
  266. for (let index = 1; index < points.length; index++) {
  267. const path0 = points[index - 1];
  268. const path1 = points[index];
  269. const path = {
  270. path0,
  271. path1,
  272. isSpread: false,
  273. spreadDirection: 0,
  274. };
  275. if (airIn) {
  276. // 首个线段需要扩散,由大变小
  277. path.isSpread = index === 1;
  278. path.spreadDirection = -1;
  279. }
  280. if (airOut) {
  281. // 首个线段需要扩散,由小变大
  282. path.isSpread = index === points.length - 1;
  283. path.spreadDirection = 1;
  284. }
  285. result.push(path);
  286. }
  287. return result;
  288. }
  289. }
  290. export default ModelContext;