Browse Source

沙盘3D修改

刘靖诗 3 years ago
parent
commit
a2fd3006c3

BIN
public/static/3d/beam-texture-red-half.png


BIN
public/static/3d/beam-texture-red.png


File diff suppressed because it is too large
+ 1329 - 0
public/static/3d/light.gltf


BIN
public/static/3d/light_binary.bin


+ 1 - 1
src/views/SandTable/SandTable.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="sand-table">
     <img :src="require('@assets/png/3dback.png')" class="i3dback" />
-    <img :src="require('@assets/png/3dcloud.png')" class="i3dcloud" />
+    <!-- <img :src="require('@assets/png/3dcloud.png')" class="i3dcloud" /> -->
     <StBack></StBack>
     <ThreeModel1 class="three-model-layer" @when="when"></ThreeModel1>
     <div class="sand-table-left" v-if="showPanel">

+ 328 - 66
src/views/SandTable/component/ThreeModel1.vue

@@ -1,5 +1,5 @@
 <template>
-    <div class="three-model">
+    <div class="three-model" @click="clickEvent">
         <loading ref="pageLoading"></loading>
         <div class="map-3d" :style="'transform: rotate('+mapDeg+'deg);'">
             <img :src="require('@assets/png/3dmap.png')" alt="">
@@ -11,7 +11,37 @@
                 v-show="info.show"
                 :style="'left: ' + info.x + 'px; top: ' + info.y + 'px;'"
             >
-                <div class="fan-name">{{info.name}}</div>
+                <div class="fan-name pointer" @click.stop="info.click">{{info.name}}</div>
+            </div>
+            <div
+                class="three-html-dom build-info"
+                :id="fanInfoLayer.id"
+                v-show="fanInfoLayer.show"
+                :style="'left: ' + fanInfoLayer.x + 'px; top: ' + fanInfoLayer.y + 'px;'"
+            >
+                <div class="build-info-close" @click.stop="hideFanInfo">
+                    <i class="el-icon-close"></i>
+                </div>
+                <div class="build-info-item purple" :style="'left: '+circleXY[0].x+'px; top: -'+circleXY[0].y+'px'">
+                    <div class="build-info-item-num">97</div>
+                    <div class="build-info-item-text">健康度</div>
+                </div>
+                <div class="build-info-item blue" :style="'left: '+circleXY[1].x+'px; top: -'+circleXY[1].y+'px'">
+                    <div class="build-info-item-num">97</div>
+                    <div class="build-info-item-text">优</div>
+                </div>
+                <div class="build-info-item yellow" :style="'left: '+circleXY[2].x+'px; top: -'+circleXY[2].y+'px'">
+                    <div class="build-info-item-num">97</div>
+                    <div class="build-info-item-text">良</div>
+                </div>
+                <div class="build-info-item orange" :style="'left: '+circleXY[3].x+'px; top: -'+circleXY[3].y+'px'">
+                    <div class="build-info-item-num">97</div>
+                    <div class="build-info-item-text">差</div>
+                </div>
+                <div class="build-info-item red" :style="'left: '+circleXY[4].x+'px; top: -'+circleXY[4].y+'px'">
+                    <div class="build-info-item-num">97</div>
+                    <div class="build-info-item-text">故障</div>
+                </div>
             </div>
         </div>
     </div>
@@ -28,7 +58,7 @@ let mixers = [];
 let clock = new THREE.Clock();
 let fanAnimates = [];
 let fans = [];
-let cylinder = null;
+// let cylinder = null;
 export default {
     // 名称
     name: "ThreeModel1",
@@ -56,6 +86,7 @@ export default {
                     oy: 10,
                     position: null,
                     name: "麻黄山",
+                    click: function () { console.log("麻黄山") },
                 },
                 { // 牛首山
                     id: "fan-nss",
@@ -66,6 +97,7 @@ export default {
                     oy: 0,
                     position: null,
                     name: "牛首山",
+                    click: function () { console.log("牛首山") },
                 },
                 { // 青山
                     id: "fan-qs",
@@ -76,6 +108,7 @@ export default {
                     oy: 10,
                     position: null,
                     name: "青山",
+                    click: function () { console.log("青山") },
                 },
                 { // 石板泉
                     id: "fan-sbq",
@@ -86,6 +119,7 @@ export default {
                     oy: 0,
                     position: null,
                     name: "石板泉",
+                    click: function () { console.log("石板泉") },
                 },
                 { // 香山
                     id: "fan-qs",
@@ -96,66 +130,79 @@ export default {
                     oy: 20,
                     position: null,
                     name: "香山",
+                    click: function () { console.log("香山") },
                 },
                 { // 大武口
                     id: "light-dwk",
                     x: 0,
                     y: 0,
                     show: true,
-                    ox: 0,
-                    oy: 110,
+                    ox: 350,
+                    oy: 80,
                     position: null,
                     name: "大武口",
+                    click: function () { console.log("大武口") },
                 },
                 { // 平罗
                     id: "light-pl",
                     x: 0,
                     y: 0,
                     show: true,
-                    ox: 30,
-                    oy: 100,
+                    ox: 350,
+                    oy: 80,
                     position: null,
                     name: "平罗",
+                    click: function () { console.log("平罗") },
                 },
                 { // 马场湖
                     id: "light-mch",
                     x: 0,
                     y: 0,
                     show: true,
-                    ox: -120,
-                    oy: 110,
+                    ox: 250,
+                    oy: 80,
                     position: null,
                     name: "马场湖",
+                    click: function () { console.log("马场湖") },
                 },
                 { // 宣和
                     id: "light-xh",
                     x: 0,
                     y: 0,
                     show: true,
-                    ox: -90,
-                    oy: 120,
+                    ox: 280,
+                    oy: 90,
                     position: null,
                     name: "宣和",
+                    click: function () { console.log("宣和") },
                 },
                 { // 海子井
                     id: "light-hzj",
                     x: 0,
                     y: 0,
                     show: true,
-                    ox: -80,
-                    oy: 150,
+                    ox: 310,
+                    oy: 120,
                     position: null,
                     name: "海子井",
+                    click: function () { console.log("海子井") },
                 },
             ],
+            fanInfoLayer: {
+                id: "fan-info",
+                x: 0,
+                y: 0,
+                show: false,
+                ox: 70,
+                oy: -40,
+                position: null,
+            },
             circleXY: [
-                { x: -(200*Math.sin(0/180*Math.PI)), y: -(200*Math.cos(0/180*Math.PI)) },
-                { x: -(200*Math.sin(-30/180*Math.PI)), y: -(200*Math.cos(-30/180*Math.PI)) },
-                { x: -(200*Math.sin(-60/180*Math.PI)), y: -(200*Math.cos(-60/180*Math.PI)) },
-                { x: -(200*Math.sin(-90/180*Math.PI)), y: -(200*Math.cos(-90/180*Math.PI)) },
-                { x: -(200*Math.sin(-120/180*Math.PI)), y: -(200*Math.cos(-120/180*Math.PI)) },
-                { x: -(200*Math.sin(-150/180*Math.PI)), y: -(200*Math.cos(-150/180*Math.PI)) },
-                { x: -(200*Math.sin(-180/180*Math.PI)), y: -(200*Math.cos(-180/180*Math.PI)) },
+                { x: (130*Math.cos(180/180*Math.PI)-33), y: (130*Math.sin(180/180*Math.PI)+33) },
+                { x: (130*Math.cos(135/180*Math.PI)-33), y: (130*Math.sin(135/180*Math.PI)+33) },
+                { x: (130*Math.cos(90/180*Math.PI)-33), y: (130*Math.sin(90/180*Math.PI)+33) },
+                { x: (130*Math.cos(45/180*Math.PI)-33), y: (130*Math.sin(45/180*Math.PI)+33) },
+                { x: (130*Math.cos(0/180*Math.PI)-33), y: (130*Math.sin(0/180*Math.PI)+33) },
             ],
             colors: [
                 { colorName: 'green', state: 'dj', stateName: '待机', color: 0x05bb4c, },
@@ -300,7 +347,7 @@ export default {
                 }
             );
             loaderGround.load(
-                "static/3d/lightvar.gltf",
+                "static/3d/light.gltf",
                 (gltf) => {
                     gltf.scene.position.set(17, 10, -12);
                     // console.log(gltf);
@@ -318,7 +365,7 @@ export default {
             loaderGround.load(
                 "static/3d/build.gltf",
                 (gltf) => {
-                    gltf.scene.position.set(10, 0, -50);
+                    gltf.scene.position.set(0, 0, -35); // 10, 0, -50    -6, 0.13, -50
                     scene.add(gltf.scene);
                     gltf.scene.rotateY(120);
                     scene.onAfterRender = () => {
@@ -351,24 +398,25 @@ export default {
                 });
             }
             let chassis = obj.getObjectByName(`${obj.name}_chassis`);
-            let color = this.colors.find(t => t.colorName == colorName || t.state == colorName || t.stateName == colorName).color;
+            // let color = this.colors.find(t => t.colorName == colorName || t.state == colorName || t.stateName == colorName).color;
             chassis.material = new THREE.MeshBasicMaterial({
-                color: color,
+                // color: color,
+                color: 0xf0f0f0,
                 opacity: 0.8,
                 transparent: true,
             });
         },
         // 改变元素的颜色(光伏)
         changeElColor: function (obj, colorName) {
-            let color = this.colors.find(t => t.colorName == colorName || t.state == colorName || t.stateName == colorName).color;
-            for (let i = 1; i <= 6; i++) {
-                let el = obj.getObjectByName(`${obj.name}_item_${i}`);
-                el.material = new THREE.MeshBasicMaterial({
-                    color: color,
-                    opacity: 0.8,
-                    transparent: true,
-                });
-            }
+            // let color = this.colors.find(t => t.colorName == colorName || t.state == colorName || t.stateName == colorName).color;
+            // for (let i = 1; i <= 6; i++) {
+            //     let el = obj.getObjectByName(`${obj.name}_item_${i}`);
+            //     el.material = new THREE.MeshBasicMaterial({
+            //         color: color,
+            //         opacity: 0.8,
+            //         transparent: true,
+            //     });
+            // }
         },
         // 设置风机name
         setFanName: function (obj, name) {
@@ -421,35 +469,35 @@ export default {
             fan_mhs.position.set(10, 5, -4);
             this.setFanName(fan_mhs, "fan_mhs");
             rootNode.add(fan_mhs);
-            // this.initCylinderGeometry(fan_mhs.position);
+            // this.initCylinderGeometry(fan_mhs);
             this.htmlLayer[0].position = fan_mhs.position;
             // 牛首山
             let fan_nss = obj.clone(true);
             fan_nss.position.set(-4, 4, -15);
             this.setFanName(fan_nss, "fan_nss");
             rootNode.add(fan_nss);
-            // this.initCylinderGeometry(fan_nss.position);
+            this.initCylinderGeometry(fan_nss); // 故障 显示红色圆柱
             this.htmlLayer[1].position = fan_nss.position;
             // 青山
             let fan_qs = obj.clone(true);
             fan_qs.position.set(15, 5, -9);
             this.setFanName(fan_qs, "fan_qs");
             rootNode.add(fan_qs);
-            // this.initCylinderGeometry(fan_qs.position);
+            // this.initCylinderGeometry(fan_qs);
             this.htmlLayer[2].position = fan_qs.position;
             // 石板泉
             let fan_sbq = obj.clone(true);
             fan_sbq.position.set(4, 5, -2);
             this.setFanName(fan_sbq, "fan_sbq");
             rootNode.add(fan_sbq);
-            // this.initCylinderGeometry(fan_sbq.position);
+            // this.initCylinderGeometry(fan_sbq);
             this.htmlLayer[3].position = fan_sbq.position;
             // 香山
             let fan_xs = obj.clone(true);
             fan_xs.position.set(-9, 7, 21);
             this.setFanName(fan_xs, "fan_xs");
             rootNode.add(fan_xs);
-            // this.initCylinderGeometry(fan_xs.position);
+            // this.initCylinderGeometry(fan_xs);
             this.htmlLayer[4].position = fan_xs.position;
 
             // 改一下颜色
@@ -497,46 +545,47 @@ export default {
         },
         // 设置光伏位置 颜色
         setLightPosition: function (rootNode, obj) {
-            let Box660 = obj.getObjectByName("Box660");
-            let Box666 = obj.getObjectByName("Box666");
-            let Box662 = obj.getObjectByName("Box662");
-            let Box663 = obj.getObjectByName("Box663");
-            let Box659 = obj.getObjectByName("Box659");
-            let Box665 = obj.getObjectByName("Box665");
-            obj.remove(Box660);
-            obj.remove(Box666);
-            obj.remove(Box662);
-            obj.remove(Box663);
-            obj.remove(Box659);
-            obj.remove(Box665);
+            obj.scale.set(200, 200, 200);
+            // let Box660 = obj.getObjectByName("Box660");
+            // let Box666 = obj.getObjectByName("Box666");
+            // let Box662 = obj.getObjectByName("Box662");
+            // let Box663 = obj.getObjectByName("Box663");
+            // let Box659 = obj.getObjectByName("Box659");
+            // let Box665 = obj.getObjectByName("Box665");
+            // obj.remove(Box660);
+            // obj.remove(Box666);
+            // obj.remove(Box662);
+            // obj.remove(Box663);
+            // obj.remove(Box659);
+            // obj.remove(Box665);
             // 大武口
             let light_dwk = obj.clone(true);
-            light_dwk.position.set(-6, 0.13, -50);
-            this.setLightName(light_dwk, "light_dwk");
+            light_dwk.position.set(-22, 0, -28); // -20 +30 // -2 0 -58 // -6, 0.13, -50    10, 0, -50
+            // this.setLightName(light_dwk, "light_dwk");
             rootNode.add(light_dwk);
             this.htmlLayer[5].position = light_dwk.position;
             // 平罗
             let light_pl = obj.clone(true);
-            light_pl.position.set(-3, 0.13, -55);
-            this.setLightName(light_pl, "light_pl");
+            light_pl.position.set(-26, 0.13, -25);
+            // this.setLightName(light_pl, "light_pl");
             rootNode.add(light_pl);
             this.htmlLayer[6].position = light_pl.position;
             // 马场湖
             let light_mch = obj.clone(true);
-            light_mch.position.set(-27, 2, -30);
-            this.setLightName(light_mch, "light_mch");
+            light_mch.position.set(-47, 2, 0);
+            // this.setLightName(light_mch, "light_mch");
             rootNode.add(light_mch);
             this.htmlLayer[7].position = light_mch.position;
             // 宣和
             let light_xh = obj.clone(true);
-            light_xh.position.set(-18, 4, -27);
-            this.setLightName(light_xh, "light_xh");
+            light_xh.position.set(-38, 4, 3);
+            // this.setLightName(light_xh, "light_xh");
             rootNode.add(light_xh);
             this.htmlLayer[8].position = light_xh.position;
             // 海子井
             let light_hzj = obj.clone(true);
-            light_hzj.position.set(-10, 2, -26);
-            this.setLightName(light_hzj, "light_hzj");
+            light_hzj.position.set(-30, 2, 4);
+            // this.setLightName(light_hzj, "light_hzj");
             rootNode.add(light_hzj);
             this.htmlLayer[9].position = light_hzj.position;
 
@@ -546,12 +595,30 @@ export default {
             this.changeElColor(light_mch, 'yx'); // 马场湖
             this.changeElColor(light_xh, 'red'); // 宣和
             this.changeElColor(light_hzj, '受累'); // 海子井
+            
+            // 设置位置
+            this.setEveryHTML();
         },
         // 创建一个圆柱
-        initCylinderGeometry: function(position, cr=2) {
-            let geometry = new THREE.CylinderGeometry(cr, cr, 7, 64);
+        initCylinderGeometry: function(obj, mode="fan") {
+            let cr = 1.7;
+            let xyz = [5.75, 6.85, -0.63];
+            switch (mode) {
+                case "fan":
+                    cr = 1.7;
+                    xyz = [5.75, 6.85, -0.63];
+                    break;
+                case "light":
+                    cr = 3;
+                    xyz = [5.75, 6.85, -0.63];
+                    break;
+            
+                default:
+                    break;
+            }
+            let geometry = new THREE.CylinderGeometry(cr, cr, 4, 128, 1, true);
             //加载纹理
-            let texture = new THREE.TextureLoader().load("static/3d/beam-texture.png");
+            let texture = new THREE.TextureLoader().load("static/3d/beam-texture-red.png");
             texture.wrapS = texture.wrapT = THREE.RepeatWrapping; //每个都重复
             texture.repeat.set(1, 1);
             texture.needsUpdate = true;
@@ -559,7 +626,7 @@ export default {
                 //圆柱侧面材质,使用纹理贴图
                 new THREE.MeshBasicMaterial({
                     map: texture,
-                    side: THREE.DoubleSide,
+                    side: THREE.FrontSide,
                     transparent: true,
                 }),
                 //圆柱顶材质
@@ -567,6 +634,7 @@ export default {
                     transparent: true,
                     opacity: 0,
                     side: THREE.DoubleSide,
+                    // side: THREE.DoubleSide,THREE.FrontSide THREE.BackSide
                 }),
                 //圆柱底材质
                 new THREE.MeshBasicMaterial({
@@ -575,8 +643,8 @@ export default {
                     side: THREE.DoubleSide,
                 }),
             ];
-            cylinder = new THREE.Mesh(geometry, materials);
-            cylinder.position.set(position.x + 5.75, position.y + 8, position.z);
+            let cylinder = new THREE.Mesh(geometry, materials);
+            cylinder.position.set(obj.position.x + xyz[0], obj.position.y + xyz[1], obj.position.z + xyz[2]);
             scene.add(cylinder);
         },
         // 设置每一个html的位置
@@ -589,6 +657,93 @@ export default {
                 }
             });
         },
+        // 判断时候是可点击对象 返回null 或object
+        getClickObject: function(intersects) {
+            const names = ["fan"];
+            const namesRoot = ["fanvar"];
+            let x = true;
+            let obj = null;
+            for (let intersect of intersects) {
+                let temObj = intersect.object;
+                while (x) {
+                    for (let name of names) {
+                        if (temObj.name.indexOf(`${name}_`) == 0) {
+                            obj = temObj;
+                            x = false;
+                            break;
+                        }
+                    }
+                    if (x && temObj.parent) {
+                        temObj = temObj.parent;
+                        continue;
+                    }
+                    break;
+                }
+            }
+            if (obj) {
+                while (obj.parent && !namesRoot.includes(obj.parent.name)) {
+                    obj = obj.parent;
+                }
+            }
+            if (obj && !namesRoot.includes(obj.parent.name)) {
+                obj = null;
+            }
+            return obj;
+        },
+        // 显示风机弹出层
+        showFanInfo: function(position) {
+            this.fanInfoLayer.position = {
+                x: position.x,
+                y: position.y,
+                z: position.z,
+            };
+            const screen = this.vector3ToScreen(this.fanInfoLayer.position);
+            this.fanInfoLayer.x = screen.x + this.fanInfoLayer.ox;
+            this.fanInfoLayer.y = screen.y + this.fanInfoLayer.oy;
+            this.fanInfoLayer.show = true;
+        },
+        // 隐藏风机弹出层
+        hideFanInfo: function () {
+            this.fanInfoLayer.show = false;
+        },
+        // 点击事件
+        clickEvent: function(event) {
+            event.preventDefault();
+            let vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
+            vector = vector.unproject(camera);
+            let raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
+            let intersects = raycaster.intersectObjects(scene.children, true);
+            // console.log(intersects); // 输出点击的对象信息
+            // 找是否是可点击对象
+            let obj = this.getClickObject(intersects);
+            if (obj) {
+                this.elClick(obj.name);
+                this.showFanInfo(obj.position);
+            }
+        },
+        // 元素点击事件 绑值
+        elClick: function (name) {
+            switch (name) {
+                case "fan_mhs": // 麻黄山
+                    console.log("麻黄山");
+                    break;
+                case "fan_nss": // 牛首山
+                    console.log("牛首山");
+                    break;
+                case "fan_qs": // 青山
+                    console.log("青山");
+                    break;
+                case "fan_sbq": // 石板泉
+                    console.log("石板泉");
+                    break;
+                case "fan_xs": // 香山
+                    console.log("香山");
+                    break;
+
+                default:
+                    break;
+            }
+        },
         // 初始化云
         initCloud: function () {
             
@@ -678,7 +833,114 @@ export default {
             color: #FFFFFF;
             line-height: 22px;
         }
+
+        .build-info {
+            width: 166px;
+            height: 33px;
+            // position: relative;
+
+            .build-info-close {
+                position: absolute;
+                width: 40px;
+                height: 40px;
+                top: -20px;
+                left: -20px;
+                border-radius: 50%;
+                background: #1a1f2fd8;
+                border: 1px solid #05bb4c;
+                box-shadow: 0px 8px 17px 1px #05bb4c66;
+                cursor: pointer;
+                color: @gray-l;
+                transition: all 0.3s;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                font-size: 14px;
+
+                &:hover {
+                    color: #05bb4c;
+                }
+            }
+
+            .build-info-item {
+                width: 66px;
+                height: 66px;
+                border-radius: 50%;
+                background: #1A1F2FE5;
+                position: absolute;
+
+                &::after {
+                    content: "";
+                    position: absolute;
+                    width: 44px;
+                    height: 2px;
+                    left: calc(50% - 22px);
+                    top: calc(50% - 1px);
+                }
+
+                .build-info-item-num {
+                    font-size: 22px;
+                    font-weight: 500;
+                    color: #FFFFFF;
+                    text-align: center;
+                    line-height: 1.2;
+                    margin-top: 6px;
+                    margin-bottom: 3px;
+                }
+
+                .build-info-item-text {
+                    font-size: 12px;
+                    color: #FFFFFF;
+                    font-weight: 400;
+                    text-align: center;
+                }
+
+                &.blue {
+                    border: 2px solid #1DA0D7;
+                    &::after {
+                        background: #1DA0D7;
+                    }
+                }
+
+                &.red {
+                    border: 2px solid #FF0000;
+                    &::after {
+                        background: #FF0000;
+                    }
+                }
+
+                &.green {
+                    border: 2px solid #05BB4C;
+                    &::after {
+                        background: #05BB4C;
+                    }
+                }
+
+                &.purple {
+                    border: 2px solid #323E6F;
+                    &::after {
+                        background: #323E6F;
+                    }
+                }
+
+                &.orange {
+                    border: 2px solid #DB5520;
+                    &::after {
+                        background: #DB5520;
+                    }
+                }
+
+                &.yellow {
+                    border: 2px solid #EDB32F;
+                    &::after {
+                        background: #EDB32F;
+                    }
+                }
+            }
+        }
     
     }
+
+    
 }
 </style>