当前位置:   article > 正文

three.js如何实现简易3D机房?(三)显示信息弹框/标签_threejs使用css3drenderer弹窗

threejs使用css3drenderer弹窗

接上一篇:

three.js如何实现简易3D机房?(二)模型加载的过渡动画:http://t.csdnimg.cn/onbWY

目录

七、创建信息展示弹框

1.整体思路

(1)需求:

(2)思路:

(3)具体步骤:

创建3D渲染器

 弹框标签和样式

 创建3D弹框的公共函数

创建常亮(报警设备红色)信息框

创建随机(正常设备绿色)信息框


完整源码地址:computerRoom-demo: 3d机房源码

不完美的地方还有很多,多多包涵~

七、创建信息展示弹框

1.整体思路
(1)需求:

默认在模型加载完之后,报警设备的红色信息框持续展示;正常设备的绿色信息框,每3s轮换展示

(2)思路:

创建3D信息框的内容是相同的,只需要区分报警设备和正常设备两种情况即可,这里我是通过CSS3DObject来实现的

(3)具体步骤:
创建3D渲染

在threeD/init.js文件中

  1. // 引入CSS3渲染器CSS3DRenderer
  2. import { CSS3DRenderer } from 'three/addons/renderers/CSS3DRenderer.js';
  3. export let scene, camera, renderer, controls, css3DRenderer, width, height
  4. // 创建CSS3D渲染器
  5. export const createCSS3DRenderer = (dom) => {
  6. // 创建一个CSS3渲染器CSS3DRenderer
  7. css3DRenderer = new CSS3DRenderer();
  8. css3DRenderer.setSize(180, 200);
  9. // HTML标签<div id="dialog"></div>外面父元素叠加到canvas画布上且重合
  10. css3DRenderer.domElement.style.position = 'absolute';
  11. css3DRenderer.domElement.style.top = '0';
  12. // css3DRenderer.domElement.style.left = '0px';
  13. //设置.pointerEvents=none,解决HTML元素标签对threejs canvas画布鼠标事件的遮挡
  14. css3DRenderer.domElement.style.pointerEvents = 'none';
  15. // threeDemoRef.value.appendChild(css3DRenderer.domElement);
  16. dom.appendChild(css3DRenderer.domElement);
  17. };

 

 弹框标签和样式

以下内容都是在index.vue文件中,便于功能交互的实现,我没有单独封装,大项目可以结合情况自行抽离封装

在HTML中准备好需要用的弹框标签和样式(根据实际项目来),需要注意的是一定要用深度选择器 :deep(),不然不生效

  1. :deep(#myDialog) {
  2. font-size: 8px;
  3. .box-container {
  4. display: flex;
  5. flex-direction: column;
  6. align-items: center;
  7. justify-content: center;
  8. animation: moveUpDown 3s infinite;
  9. .title {
  10. font-family: Source Han Sans CN, Source Han Sans CN;
  11. font-weight: bold;
  12. color: #fff;
  13. }
  14. .label-text {
  15. font-family: Source Han Sans CN, Source Han Sans CN;
  16. color: #ccddff;
  17. .label-value-green {
  18. color: #5cdd2e;
  19. font-weight: bold;
  20. }
  21. .label-value-red {
  22. color: #ff4a4a;
  23. font-weight: bold;
  24. }
  25. }
  26. .tip-green {
  27. width: 80px;
  28. height: 40px;
  29. padding: 5px;
  30. display: flex;
  31. flex-direction: column;
  32. justify-content: center;
  33. background-color: rgba(22, 29, 38, 0.5);
  34. opacity: 0.9;
  35. border: 1px solid #329550;
  36. box-shadow: inset 0px 0px 15px 0px #329550;
  37. }
  38. .tip-red {
  39. width: 80px;
  40. height: 40px;
  41. padding: 5px;
  42. display: flex;
  43. flex-direction: column;
  44. justify-content: center;
  45. background-color: rgba(22, 29, 38, 0.5);
  46. opacity: 0.9;
  47. border: 1px solid #882c2c;
  48. box-shadow: inset 0px 0px 15px 0px #882c2c;
  49. }
  50. .line-green {
  51. width: 1px;
  52. height: 35px;
  53. background: linear-gradient(to bottom, rgba(28, 107, 51, 0.3), rgb(20, 195, 93), rgba(1, 165, 75, 0.89));
  54. }
  55. .line-red {
  56. width: 1px;
  57. height: 35px;
  58. background: linear-gradient(to bottom, rgba(123, 44, 28, 0.3), rgba(255, 82, 82, 1), rgba(255, 48, 48, 0.89));
  59. }
  60. }
  61. }
  62. // 动画
  63. @keyframes moveUpDown {
  64. 0% {
  65. transform: translateY(0);
  66. }
  67. 50% {
  68. transform: translateY(8px);
  69. }
  70. 100% {
  71. transform: translateY(0);
  72. }
  73. }
 创建3D弹框的公共函数
  1. const insertDialogHtml = (obj: any, item: any) => {
  2. // 多个标签-需要克隆复制一份
  3. const div: any = dialogRef.value.cloneNode();
  4. div.innerHTML = `
  5. <div class="box-container">
  6. <div class=${item.status == 0 ? 'tip-green' : 'tip-red'} >
  7. <div class="title">设备名称 : ${item.name}</div>
  8. <div class="label-text">
  9. 温度 :
  10. <span class="mr5" class=${item.tempState == '0' ? 'label-value-green' : 'label-value-red'}>
  11. ${item.temperature}
  12. </span>
  13. <span class=${item.tempState == '0' ? 'label-value-green' : 'label-value-red'}>
  14. ${item.tempState == '0' ? '正常' : '报警'}
  15. </span>
  16. </div>
  17. <div class="label-text">
  18. 漏水 :
  19. <span class="mr5" class=${item.leakageState == '0' ? 'label-value-green' : 'label-value-red'}>
  20. ${item.leakage}
  21. </span>
  22. <span class=${item.leakageState == '0' ? 'label-value-green' : 'label-value-red'}>
  23. ${item.leakageState == '0' ? '正常' : '报警'}
  24. </span>
  25. </div>
  26. </div>
  27. <div class=${item.status == 0 ? 'line-green' : 'line-red'}></div>
  28. </div>
  29. `;
  30. // HTML元素转化为threejs的CSS3对象
  31. const dialog = new CSS3DObject(div);
  32. //避免标签遮挡canvas鼠标事件
  33. div.style.pointerEvents = 'none';
  34. dialog.name = obj.name + 'dialog';
  35. dialog.scale.set(0.1, 0.1, 1);
  36. dialog.position.set(0, 6, 0);
  37. // 判断是否需要旋转
  38. // const nameList = ['AU04', 'AU07', 'AU08', 'AU09', 'AU10', 'AU11', 'AU16', 'AU17', 'AU18', 'AU19', 'AU20'];
  39. // if (nameList.includes(obj.name)) {
  40. // dialog.rotation.set(0, -Math.PI / 2, 0);
  41. // }
  42. obj.add(dialog);
  43. };

准备好需要用的变量,为了方便展示,我这里简单写了一些假数据,实际需要从后端接口获取

  1. const state: any = reactive({
  2. loading: true, // 是否开启加载动画
  3. progress: 0, // 模型加载进度
  4. randomObject: null, // 随机正常设备
  5. selectedDevice: null, // 点击选中的设备
  6. intervalId: null, // 定时器
  7. allDeviceObjList: [], // 所有设备obj
  8. // 报警设备
  9. alarmInfo: [
  10. {
  11. name: 'AU02',
  12. temperature: '35℃', // 温度
  13. tempState: '1', // 温度报警状态 0正常 1报警
  14. leakage: '58', // 漏水
  15. leakageState: '1', // 漏水报警状态 0正常 1报警
  16. status: 1,
  17. },
  18. {
  19. name: 'AU08',
  20. temperature: '38℃', // 温度
  21. tempState: '1', // 温度报警状态 0正常 1报警
  22. leakage: '60', // 漏水
  23. leakageState: '1', // 漏水报警状态 0正常 1报警
  24. status: 1,
  25. },
  26. {
  27. name: 'AU18',
  28. temperature: '40℃', // 温度
  29. tempState: '1', // 温度报警状态 0正常 1报警
  30. leakage: '72', // 漏水
  31. leakageState: '1', // 漏水报警状态 0正常 1报警
  32. status: 1,
  33. },
  34. ],
  35. // 正常设备
  36. normalInfo: [
  37. {
  38. name: 'AU01',
  39. temperature: '35℃', // 温度
  40. tempState: '0', // 温度报警状态 0正常 1报警
  41. leakage: '58', // 漏水
  42. leakageState: '0', // 漏水报警状态 0正常 1报警
  43. status: 0,
  44. },
  45. {
  46. name: 'AU03',
  47. temperature: '35℃', // 温度
  48. tempState: '0', // 温度报警状态 0正常 1报警
  49. leakage: '58', // 漏水
  50. leakageState: '0', // 漏水报警状态 0正常 1报警
  51. status: 0,
  52. },
  53. {
  54. name: 'AU04',
  55. temperature: '35℃', // 温度
  56. tempState: '0', // 温度报警状态 0正常 1报警
  57. leakage: '58', // 漏水
  58. leakageState: '0', // 漏水报警状态 0正常 1报警
  59. status: 0,
  60. },
  61. {
  62. name: 'AU05',
  63. temperature: '35℃', // 温度
  64. tempState: '0', // 温度报警状态 0正常 1报警
  65. leakage: '58', // 漏水
  66. leakageState: '0', // 漏水报警状态 0正常 1报警
  67. status: 0,
  68. },
  69. {
  70. name: 'AU06',
  71. temperature: '35℃', // 温度
  72. tempState: '0', // 温度报警状态 0正常 1报警
  73. leakage: '58', // 漏水
  74. leakageState: '0', // 漏水报警状态 0正常 1报警
  75. status: 0,
  76. },
  77. {
  78. name: 'AU07',
  79. temperature: '35℃', // 温度
  80. tempState: '0', // 温度报警状态 0正常 1报警
  81. leakage: '58', // 漏水
  82. leakageState: '0', // 漏水报警状态 0正常 1报警
  83. status: 0,
  84. },
  85. {
  86. name: 'AU09',
  87. temperature: '35℃', // 温度
  88. tempState: '0', // 温度报警状态 0正常 1报警
  89. leakage: '58', // 漏水
  90. leakageState: '0', // 漏水报警状态 0正常 1报警
  91. status: 0,
  92. },
  93. {
  94. name: 'AU10',
  95. temperature: '35℃', // 温度
  96. tempState: '0', // 温度报警状态 0正常 1报警
  97. leakage: '58', // 漏水
  98. leakageState: '0', // 漏水报警状态 0正常 1报警
  99. status: 0,
  100. },
  101. {
  102. name: 'AU11',
  103. temperature: '35℃', // 温度
  104. tempState: '0', // 温度报警状态 0正常 1报警
  105. leakage: '58', // 漏水
  106. leakageState: '0', // 漏水报警状态 0正常 1报警
  107. status: 0,
  108. },
  109. {
  110. name: 'AU12',
  111. temperature: '35℃', // 温度
  112. tempState: '0', // 温度报警状态 0正常 1报警
  113. leakage: '58', // 漏水
  114. leakageState: '0', // 漏水报警状态 0正常 1报警
  115. status: 0,
  116. },
  117. {
  118. name: 'AU13',
  119. temperature: '35℃', // 温度
  120. tempState: '0', // 温度报警状态 0正常 1报警
  121. leakage: '58', // 漏水
  122. leakageState: '0', // 漏水报警状态 0正常 1报警
  123. status: 0,
  124. },
  125. {
  126. name: 'AU14',
  127. temperature: '35℃', // 温度
  128. tempState: '0', // 温度报警状态 0正常 1报警
  129. leakage: '58', // 漏水
  130. leakageState: '0', // 漏水报警状态 0正常 1报警
  131. status: 0,
  132. },
  133. {
  134. name: 'AU15',
  135. temperature: '35℃', // 温度
  136. tempState: '0', // 温度报警状态 0正常 1报警
  137. leakage: '58', // 漏水
  138. leakageState: '0', // 漏水报警状态 0正常 1报警
  139. status: 0,
  140. },
  141. {
  142. name: 'AU16',
  143. temperature: '35℃', // 温度
  144. tempState: '0', // 温度报警状态 0正常 1报警
  145. leakage: '58', // 漏水
  146. leakageState: '0', // 漏水报警状态 0正常 1报警
  147. status: 0,
  148. },
  149. {
  150. name: 'AU17',
  151. temperature: '35℃', // 温度
  152. tempState: '0', // 温度报警状态 0正常 1报警
  153. leakage: '58', // 漏水
  154. leakageState: '0', // 漏水报警状态 0正常 1报警
  155. status: 0,
  156. },
  157. {
  158. name: 'AU19',
  159. temperature: '35℃', // 温度
  160. tempState: '0', // 温度报警状态 0正常 1报警
  161. leakage: '58', // 漏水
  162. leakageState: '0', // 漏水报警状态 0正常 1报警
  163. status: 0,
  164. },
  165. {
  166. name: 'AU20',
  167. temperature: '35℃', // 温度
  168. tempState: '0', // 温度报警状态 0正常 1报警
  169. leakage: '58', // 漏水
  170. leakageState: '0', // 漏水报警状态 0正常 1报警
  171. status: 0,
  172. },
  173. {
  174. name: 'AU21',
  175. temperature: '35℃', // 温度
  176. tempState: '0', // 温度报警状态 0正常 1报警
  177. leakage: '58', // 漏水
  178. leakageState: '0', // 漏水报警状态 0正常 1报警
  179. status: 0,
  180. },
  181. {
  182. name: 'AU22',
  183. temperature: '35℃', // 温度
  184. tempState: '0', // 温度报警状态 0正常 1报警
  185. leakage: '58', // 漏水
  186. leakageState: '0', // 漏水报警状态 0正常 1报警
  187. status: 0,
  188. },
  189. ],
  190. });
创建常亮(报警设备红色)信息框
  1. import { createCSS3DRenderer } from './component/threeD/init.js';
  2. onMounted(async () => {
  3. init(threeDemoRef.value);
  4. importModel();
  5. createControls();
  6. initLight();
  7. createCSS3DRenderer(threeDemoRef.value);
  8. watchDom(threeDemoRef.value);
  9. renderResize(threeDemoRef.value);
  10. renderLoop();
  11. });
  12. const createAlarmDialog = () => {
  13. state.allDeviceObjList = [];
  14. model.traverse((obj: any) => {
  15. // 筛选出报警设备
  16. if (obj.name.includes('AU')) {
  17. state.allDeviceObjList.push(obj);
  18. // 报警数据持续展示
  19. state.alarmInfo.forEach((item: any) => {
  20. if (item.name == obj.name) {
  21. insertDialogHtml(obj, item);
  22. }
  23. });
  24. }
  25. });
  26. };
创建随机(正常设备绿色)信息框
  1. const createNormalDialog = () => {
  2. // 过滤出正常设备的obj
  3. const filteredEquipment = state.allDeviceObjList.filter((item: any) => !['AU02', 'AU08', 'AU18'].includes(item.name));
  4. let index = state.normalInfo.length - 1;
  5. state.intervalId = setInterval(() => {
  6. // 移除上一个dialog
  7. clearDialog();
  8. index = index == state.normalInfo.length - 1 ? 0 : ++index;
  9. const randomInfo = state.normalInfo[index];
  10. const randomObject = filteredEquipment.filter((item: any) => item.name == randomInfo.name);
  11. state.randomObject = randomObject[0];
  12. insertDialogHtml(state.randomObject, randomInfo);
  13. }, 3000);
  14. };
  15. // 清除前一个随机框
  16. const clearDialog = () => {
  17. if (state.randomObject) {
  18. const currentRandomObject = model.getObjectByName(state.randomObject.name + 'dialog');
  19. currentRandomObject ? currentRandomObject.parent.remove(currentRandomObject) : '';
  20. state.randomObject = null;
  21. }
  22. };

在模型加载函数中调用

  1. // 创建3D弹框
  2. const create3DDialog = () => {
  3. createAlarmDialog();
  4. createNormalDialog();
  5. };

接下一篇:

three.js如何实现简易3D机房?(四)点击事件:http://t.csdnimg.cn/Fzpxk 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/928586
推荐阅读
相关标签
  

闽ICP备14008679号