浏览代码

大屏3D实现

dengrui 4 月之前
父节点
当前提交
0a5eaa3194

+ 1 - 0
package.json

@@ -58,6 +58,7 @@
     "element-plus": "^2.9.1",
     "exceljs": "^4.4.0",
     "file-saver": "^2.0.5",
+    "gsap": "^3.12.7",
     "html2canvas": "^1.4.1",
     "jspdf": "^2.5.2",
     "lodash-es": "^4.17.21",

+ 9 - 0
pnpm-lock.yaml

@@ -62,6 +62,9 @@ importers:
       file-saver:
         specifier: ^2.0.5
         version: 2.0.5
+      gsap:
+        specifier: ^3.12.7
+        version: 3.12.7
       html2canvas:
         specifier: ^1.4.1
         version: 1.4.1
@@ -2923,6 +2926,9 @@ packages:
   graphemer@1.4.0:
     resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
 
+  gsap@3.12.7:
+    resolution: {integrity: sha512-V4GsyVamhmKefvcAKaoy0h6si0xX7ogwBoBSs2CTJwt7luW0oZzC0LhdkyuKV8PJAXr7Yaj8pMjCKD4GJ+eEMg==}
+
   gzip-size@6.0.0:
     resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==}
     engines: {node: '>=10'}
@@ -5244,6 +5250,7 @@ packages:
   yaeti@0.0.6:
     resolution: {integrity: sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==}
     engines: {node: '>=0.10.32'}
+    deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
 
   yallist@3.1.1:
     resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -8272,6 +8279,8 @@ snapshots:
 
   graphemer@1.4.0: {}
 
+  gsap@3.12.7: {}
+
   gzip-size@6.0.0:
     dependencies:
       duplexer: 0.1.2

+ 7 - 0
src/api/bigScreen/index.ts

@@ -104,3 +104,10 @@ export function getTodayError() {
     method: "post",
   });
 }
+//设备状态
+export function getOnlineInfo() {
+  return request({
+    url: "/mes/web/station/onlineInfo",
+    method: "get",
+  });
+}

+ 1 - 1
src/views/report/statistics/screens/common-header.vue

@@ -12,7 +12,7 @@
           </span>
         </div>
       </div>
-      <div class="temperature">32<span style="font-size: 1vw">℃</span></div>
+      <!-- <div class="temperature">32<span style="font-size: 1vw">℃</span></div> -->
     </div>
   </div>
 </template>

+ 1 - 1
src/views/report/statistics/screens/common-headerB.vue

@@ -12,7 +12,7 @@
           </span>
         </div>
       </div>
-      <div class="temperature">32<span style="font-size: 1vw">℃</span></div>
+      <!-- <div class="temperature">32<span style="font-size: 1vw">℃</span></div> -->
     </div>
   </div>
 </template>

+ 3 - 3
src/views/report/statistics/screens/line/index.vue

@@ -63,8 +63,8 @@
         </ShowScroll>
       </div>
       <div class="middle">
-        <div class="item">
-          <el-image src="/images/111.gif" style="width: 100%; height: 100%" />
+        <div class="item" style="padding: 0">
+          <Middle3D />
         </div>
         <div class="item">
           <TitleHeaderB title="自动化设备状态" />
@@ -96,7 +96,7 @@
                   <div class="info">
                     <div class="info1">{{ item.deviceName }}</div>
                     <div class="info2">
-<!--                      <div class="text2">
+                      <!--                      <div class="text2">
                         今日稼动
                         <span class="nums">{{ item.utilizationRate }}</span>
                       </div>

+ 244 - 28
src/views/report/statistics/screens/line/middle3D.vue

@@ -1,47 +1,263 @@
 <template>
-  <div ref="containerRef" class="middle3D"></div>
+  <div style="position: relative">
+    <div ref="containerRef" id="middle3D"></div>
+  </div>
 </template>
 
-<script lang="ts" setup>
-import ThreeHelper from "@/hooks/ThreeHelper";
+<script setup>
+import { getOnlineInfo } from "@/api/bigScreen";
 import * as THREE from "three";
+import * as TWEEN from "three/examples/jsm/libs/tween.module.js";
+import gsap from "gsap";
+//导入轨道控制器
+import { OrbitControls } from "three/addons/controls/OrbitControls.js";
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
+import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
 
-const containerRef = ref<HTMLDivElement>(null);
-
-const threeHelper = new ThreeHelper();
+const containerRef = ref(null);
+//相机y轴高度
+const cameraYVal = ref(8);
+//相机x轴半宽度
+const cameraXVal = ref(20);
+//相机z轴高度
+const cameraZVal = ref(6);
+const modleColor = ref({
+  online: "0x06ffa5",
+  outline: "0xdb4848",
+});
+const infoArray = ref([]);
+const getInfoArray = async () => {
+  const { data } = await getOnlineInfo();
+  infoArray.value = data;
+};
 
-onMounted(() => {
-  threeHelper.creatRenderer(containerRef.value);
+const init3D = () => {
+  //创建场景
+  const scene = new THREE.Scene();
+  //渲染函数
+  const render = () => {
+    renderer.render(scene, camera);
+    requestAnimationFrame(render);
+    TWEEN.update();
+  };
+  //灯光
+  const lights = () => {
+    //给场景增加环境光
+    let Light1 = new THREE.DirectionalLight(0xffffff, 1);
+    Light1.position.set(0, 0, 10);
+    scene.add(Light1);
+    let Light2 = new THREE.DirectionalLight(0xffffff, 1);
+    Light2.position.set(0, 0, -10);
+    scene.add(Light2);
+    let Light3 = new THREE.DirectionalLight(0xffffff, 1);
+    Light3.position.set(10, 0, 0);
+    scene.add(Light3);
+    let Light4 = new THREE.DirectionalLight(0xffffff, 1);
+    Light4.position.set(-10, 0, 10);
+    scene.add(Light4);
+    let Light5 = new THREE.DirectionalLight(0xffffff, 1);
+    Light5.position.set(0, 10, 0);
+    scene.add(Light5);
+    let Light6 = new THREE.DirectionalLight(0xffffff, 1);
+    Light6.position.set(0, -10, 0);
+    scene.add(Light6);
+    let Light7 = new THREE.DirectionalLight(0xffffff, 1);
+    Light7.position.set(5, 10, 0);
+    scene.add(Light7);
+    let Light8 = new THREE.DirectionalLight(0xffffff, 1);
+    Light8.position.set(0, 10, 5);
+    scene.add(Light8);
+    let Light9 = new THREE.DirectionalLight(0xffffff, 1);
+    Light9.position.set(-5, 10, 0);
+    scene.add(Light9);
+  };
+  lights();
+  //创建一个相机
+  const camera = new THREE.PerspectiveCamera(
+    45, //视角
+    document.getElementById("middle3D").clientWidth /
+      document.getElementById("middle3D").clientHeight,
+    0.1, //近平面
+    1000 //远平面
+  );
+  camera.position.set(0, 15, 8);
+  const renderer = new THREE.WebGLRenderer({
+    //抗锯齿
+    antialias: true,
+  });
+  renderer.setSize(
+    document.getElementById("middle3D").clientWidth,
+    document.getElementById("middle3D").clientHeight
+  );
+  containerRef.value.appendChild(renderer.domElement);
+  scene.fog = new THREE.Fog(0x000000, 0.1, 50);
+  scene.background = new THREE.Color(0x000000);
+  //
+  const controls = new OrbitControls(camera, renderer.domElement);
+  controls.update();
+  const loader = new GLTFLoader();
+  render();
+  //圆锥体动画
+  function animateModel(model) {
+    // 创建一个时间轴
+    const tl = gsap.timeline({
+      repeat: -1, // 无限重复
+      yoyo: true, // 往复运动
+      ease: "power1.inOut", // 缓动函数
+    });
 
-  threeHelper.loadFBX("/black_box.fbx", () => {
-    threeHelper.scene.traverse((child) => {
-      if (child instanceof THREE.Mesh) {
-        console.log("traverse", child.material);
-        if (child.material.length > 1) {
-          child.material[0].color.set(0x00ff00);
-          child.material[1].color.set(0x00ff00);
-          child.material[2].color.set(0x00ff00);
-          child.material[3].color.set(0x00ff00);
-          threeHelper.resizeRender(containerRef.value);
-        }
+    // 添加动画到时间轴
+    tl.to(model.position, {
+      duration: 1, // 动画持续时间
+      y: 4, // 上升到y=3.5的位置
+      ease: "power1.inOut",
+    });
+  }
+  //单个巡航实现函数
+  function timeout(time, option, i) {
+    setTimeout(() => {
+      camera.position.set(
+        cameraPositions[i].reset.x,
+        cameraYVal.value,
+        cameraPositions[i].reset.z
+      );
+      camera.lookAt(0, 0, 0);
+      camera.position.set(
+        cameraPositions[i].start.x,
+        cameraYVal.value,
+        cameraPositions[i].start.z
+      );
+      const tl = gsap.timeline({
+        defaults: { ease: "linear" },
+      });
+      tl.to(camera.position, option);
+    }, time);
+  }
+  //车间巡航动画
+  function animateCamera() {
+    let timer = 0;
+    for (let i = 0; i < cameraPositions.length; i++) {
+      // 创建一个时间轴
+      if (i !== 0) {
+        timer = timer + cameraPositions[i - 1].time * 1000;
+      }
+      const object = {
+        duration: cameraPositions[i].time,
+        [cameraPositions[i].change.lable]: cameraPositions[i].change.value,
+      };
+      timeout(timer, object, i);
+    }
+    // 添加动画到时间轴
+  }
+  // const gui = new GUI();
+  // gui.add(camera.position, "x").min(-50).max(50).step(0.1).name("相机x");
+  // gui.add(camera.position, "y").min(-50).max(50).step(0.1).name("相机y");
+  // gui.add(camera.position, "z").min(-50).max(50).step(0.1).name("相机z");
+  // gui.add(camera, "fov").min(0).max(50).step(0.1).name("相机视角");
+  //加载模型
+  loader.load("/3dmodel/XF.glb", (gltf) => {
+    console.log(gltf);
+    scene.add(gltf.scene);
+    gltf.scene.position.set(2.8, 0, 0);
+    const motorModel = gltf.scene;
+    motorModel.traverse((obj) => {
+      if (/^锥体-/.test(obj.name)) {
+        obj.material.color.set(0x06ffa5);
+        animateModel(obj);
       }
     });
+    animateCamera();
+    setInterval(() => {
+      animateCamera();
+    }, 30000);
   });
+  //巡航路线 ->reset 为调整相机lookat向量点 start 为调整后相机位开始位置 change 为本次动画调整的坐标和距离
+  const cameraPositions = [
+    {
+      reset: {
+        x: 0,
+        z: cameraZVal.value,
+      },
+      start: {
+        x: cameraXVal.value,
+        z: cameraZVal.value,
+      },
+      change: {
+        lable: "x",
+        value: -cameraXVal.value,
+      },
+      time: 10,
+    },
+    {
+      reset: {
+        x: -cameraXVal.value,
+        z: 0,
+      },
+      start: {
+        x: -cameraXVal.value,
+        z: cameraZVal.value,
+      },
+      change: {
+        lable: "z",
+        value: -cameraZVal.value,
+      },
+      time: 5,
+    },
+    {
+      reset: {
+        x: 0,
+        z: -cameraZVal.value,
+      },
+      start: {
+        x: -cameraXVal.value,
+        z: -cameraZVal.value,
+      },
+      change: {
+        lable: "x",
+        value: cameraXVal.value,
+      },
+      time: 10,
+    },
+    {
+      reset: {
+        x: cameraXVal.value,
+        z: 0,
+      },
+      start: {
+        x: cameraXVal.value,
+        z: -cameraZVal.value,
+      },
+      change: {
+        lable: "z",
+        value: cameraZVal.value,
+      },
+      time: 5,
+    },
+  ];
+  //监听窗口变化
+  window.addEventListener("resize", () => {
+    renderer.setSize(
+      document.getElementById("middle3D").clientWidth,
+      document.getElementById("middle3D").clientHeight
+    );
+    camera.aspect =
+      document.getElementById("middle3D").clientWidth /
+      document.getElementById("middle3D").clientHeight;
+    camera.updateProjectionMatrix();
+  });
+};
 
-  // threeHelper.addOutline();
-});
-
-window.addEventListener("resize", () => {
-  threeHelper.resizeRender(containerRef.value);
-  // threeHelper.addOutline();
+onMounted(() => {
+  getInfoArray();
+  init3D();
 });
 </script>
 
 <style lang="scss" scoped>
-.middle3D {
+#middle3D {
   height: 29vh;
   width: 100%;
-  padding: 0 !important;
-  border: 2px solid red;
+  position: absolute;
+  z-index: 5;
 }
 </style>