echarts实现伪3D地图及下钻到省

345 阅读5分钟

实现效果如下图

_2024-09-23 111715.gif 使用工具:

项目目录结构

image.png

  • country中存放中国json数据
  • province中存放省份json数据
  • china-data.js为各省份信息,地图下钻时用于获取对应省份json文件名

南海群岛放在右下角框问题: 在geo中配置regions

geo: {
          show: true,
          map: mapName,
          regions: [
            {
              name: "南海诸岛",
              value: 0,
              itemStyle: {
                normal: {
                  opacity: 0,
                  label: {
                    show: false
                  }
                }
              }
            }
          ],
          roam: false,
          top: "12%",
          //   left: "11%",
          layoutCenter: ["30%", "30%"],
          zoom: 1.2,
          aspectScale: 1.11,
          label: {
            normal: {
              show: false
            },
            emphasis: {
              show: false
            }
          },
          itemStyle: {
            normal: {
              opacity: 1, //图形透明度 0 - 1
              borderColor: "#09a5df00", //图形的描边颜色
              borderWidth: 2, //描边线宽。为 0 时无描边。
              borderType: "solid", //柱条的描边类型,默认为实线,支持 'solid', 'dashed', 'dotted'。
              areaColor: "#17395b" //图形的颜色 #eee
            },
            emphasis: {
              borderColor: "#09a5df00",
              borderWidth: 2,
              areaColor: "#17395b",
              label: {
                show: false
              }
            }
          },

          tooltip: {
            formatter: o => {
              return
            }
          }
        },

海南省隐藏市级线 删掉中国json数据中海南省数据中的市级数据即可

关于地图点击事件是监听地图的click事件

this.eMap.on("click", e => {
      let mapData = chinaData.find(item => item.name == e.name);
      if (mapData) {
        this.mapTitle = e.name;
        this.mapName = mapData.mapName;
        this.geoJson = require(`./province/${this.mapName}.json`);
        this.initMap(this.mapName, this.geoJson);
      }
    });

完整vue文件如下:

<template>
  <div class="mapBox">
    <div id="eMap" style="width: 79%; height: 95%"></div>
    <div class="mapRouter">
      <span class="back" @click="backChina">中国</span>
      <span v-if="mapTitle != ''">/</span>
      <span class="nextMap">{{ mapTitle ? mapTitle : "" }}</span>
    </div>
  </div>
</template>
<script>
import china from "./country/china.json";
import chinaData from "./china-data.js";
import * as echarts from "echarts";
import micon_jifang from './img/micon_jifang.svg'
import micon_offline from './img/micon_offline.svg'
import micon_online from './img/micon_online.svg'
import { debounce } from '@/utils';

export default {
  data () {
    return {
      eMap: null,
      china: china,
      mapOption: {},
      mapData: [
        {
          lat: 28.676493,
          lng: 115.892151,
          parent: "江西省",
          name: "兴图新科",
          type: "jifang",
          project: "兴图新科",
          cabinetNum: 3,
          deviceNum: 30,
          onlineDevice: "20/30",
          offlineDevice: "1/30",
          inFlow: 30,
          outFlow: 20,
          position: "江西省南昌市"
        },
        {
          lat: 34.757975,
          lng: 113.665412,
          parent: "河南省",
          name: "视频传输网关",
          project: "兴图新科",
          ip: "11.15.83.10",
          position: "河南省郑州市",
          deviceStatus: 1,
          unitStatus: "15/16",
          type: "fdevice"
        },
        {
          lat: 30.659462,
          lng: 104.065735,
          parent: "四川省",
          name: "四川省机房",
          type: "odevice"
        },
        {
          adcode: "500000",
          people_count_2010: 28458101,
          lat: 29.533155,
          lng: 106.504962,
          parent: "重庆市",
          name: "重庆市机房",
          type: "odevice"
        }
      ],
      mapTitle: "",
      mapName: "",
      geoJson: null
    };
  },
  mounted () {
    this.initMap();
    this.eMap.on("click", e => {
      let mapData = chinaData.find(item => item.name == e.name);
      if (mapData) {
        this.mapTitle = e.name;
        this.mapName = mapData.mapName;
        this.geoJson = require(`./province/${this.mapName}.json`);
        this.initMap(this.mapName, this.geoJson);
      }
    });
    this.__resizeHandler = debounce(() => {

      if (this.eMap) {
        this.eMap.resize()
      }
    }, 100)
    window.addEventListener('resize', this.__resizeHandler)
  },
  beforeDestroy () {
    if (this.eMap) {
      echarts.dispose(this.eMap);
      this.eMap = null;
    }
  },
  methods: {
    initMap (mapName = "china", geoJson = china) {
      this.eMap = echarts.init(document.querySelector("#eMap"));
      echarts.registerMap(mapName, geoJson);
      this.mapOption = {
        // 提示框组件(可以设置在多种地方)
        tooltip: {
          show: true, //是否显示提示框组件,包括提示框浮层和 axisPointer。
          trigger: "item", //触发类型。item,axis,none
          enterable: true, //鼠标是否可进入提示框浮层中,默认为false,
          showContent: true, //是否显示提示框浮层
          triggerOn: "mousemove", //提示框触发的条件(mousemove|click|none)
          showDelay: 10, //浮层显示的延迟,单位为 ms,默认没有延迟,也不建议设置。在 triggerOn 为 'mousemove' 时有效。
          textStyle: {
            color: "white",
            fontSize: 12
          },
          confine: true,
          hideDelay: 10, //浮层隐藏的延迟
          formatter: o => {
            if (o.value) {
              let data = o.data;
              if (data.type == "jifang") {
                return `<div class="jifang_mapInfo">
                <div class="mapInfo_title">
                    <div class="title_icon"></div>
                    <div class="text overText" title="${data.project}">
                    ${data.project}
                    </div>
                </div>
                <div class="mapInfo_content">
                    <div>
                    <div class="width8">机柜数量</div>
                    <div class="width14">${data.cabinetNum} 个</div>
                  </div>
                  <div>
                    <div class="width8">设备数量</div>
                    <div class="width14">${data.deviceNum} 台</div>
                  </div>
                  <div>
                    <div class="width8">在线设备</div>
                    <div class="width14">${data.onlineDevice}</div>
                  </div>
                  <div>
                    <div class="width8">异常设备</div>
                    <div class="width14">${data.offlineDevice}</div>
                  </div>
                  <div>
                    <div class="width8">输入流量</div>
                    <div class="width14">${data.inFlow} Gbps</div>
                  </div>
                  <div>
                    <div class="width8">输出流量</div>
                    <div class="width14">${data.outFlow} Gbps</div>
                  </div>
                  <div>
                    <div class="width8">机房位置</div>
                    <div class="width14" title="${data.position}">${data.position
                  }</div>
                  </div>
                </div>
              </div>`;
              } else {
                return `<div class="device_mapInfo">
                <div class="mapInfo_title">
                    <div class="title_icon"></div>
                    <div class="text overText" title="${data.name}">
                    ${data.name}
                    </div>
                </div>
                <div class="mapInfo_content">
                    <div>
                    <div class="width8">所属项目</div>
                    <div class="width14" title="${data.project}">${data.project
                  }</div>
                  </div>
                  <div>
                    <div class="width8">设备IP</div>
                    <div class="width14">${data.ip}</div>
                  </div>
                  <div>
                    <div class="width8">设备位置</div>
                    <div class="width14" title="${data.position}">${data.position
                  }</div>
                  </div>
                  <div>
                    <div class="width8">设备状态</div>
                    <div class="width14">${data.deviceStatus == 1 ? "正常运行" : "异常停止"
                  }</div>
                  </div>
                  <div>
                    <div class="width8">单元状态</div>
                    <div class="width14">${data.unitStatus}</div>
                  </div>
                </div>
              </div>`;
              }
            }
          },
          backgroundColor: "#00a2e300", //提示框浮层的背景颜色。
          alwaysShowContent: true,
          transitionDuration: 1 //提示框浮层的移动动画过渡时间,单位是 s,设置为 0 的时候会紧跟着鼠标移动。
        },
        //地理坐标系组件。地理坐标系组件用于地图的绘制,支持在地理坐标系上绘制散点图,线集。
        geo: {
          show: true,
          map: mapName,
          regions: [
            {
              name: "南海诸岛",
              value: 0,
              itemStyle: {
                normal: {
                  opacity: 0,
                  label: {
                    show: false
                  }
                }
              }
            }
          ],
          roam: false,
          top: "12%",
          //   left: "11%",
          layoutCenter: ["30%", "30%"],
          zoom: 1.2,
          aspectScale: 1.11,
          label: {
            normal: {
              show: false
            },
            emphasis: {
              show: false
            }
          },
          itemStyle: {
            normal: {
              opacity: 1, //图形透明度 0 - 1
              borderColor: "#09a5df00", //图形的描边颜色
              borderWidth: 2, //描边线宽。为 0 时无描边。
              borderType: "solid", //柱条的描边类型,默认为实线,支持 'solid', 'dashed', 'dotted'。
              areaColor: "#17395b" //图形的颜色 #eee
            },
            emphasis: {
              borderColor: "#09a5df00",
              borderWidth: 2,
              areaColor: "#17395b",
              label: {
                show: false
              }
            }
          },

          tooltip: {
            formatter: o => {
              return
            }
          }
        },
        //系列列表。每个系列通过 type(map, scatter, bar, line, gauge, tree.....) 决定自己的图表类型
        series: [
          {
            map: mapName,
            type: "scatter",
            coordinateSystem: "geo",
            symbolSize: 0,
            silent: "none",
            data: [],
            itemStyle: {
              normal: {
                opacity: 1, //图形透明度 0 - 1
                borderColor: "#09a5df5f", //图形的描边颜色
                borderWidth: 2, //描边线宽。为 0 时无描边。
                borderType: "solid", //柱条的描边类型,默认为实线,支持 'solid', 'dashed', 'dotted'。
                areaColor: "#17395b" //图形的颜色 #eee
              }
            }
          },
          {
            map: mapName,
            type: "map",
            top: "8%",
            // left: "10.2%",
            zoom: 1.2, //当前视角的缩放比例。
            aspectScale: 1.11, //这个参数用于 scale 地图的长宽比。geoBoundingRect.width / geoBoundingRect.height * aspectScale
            roam: false, //是否开启鼠标缩放和平移漫游。默认不开启
            layoutCenter: ["30%", "30%"],
            label: {
              show: false,
              textStyle: {
                color: "white",
                fontSize: 12,
                backgroundColor: "" //文字背景色
              }
            },
            itemStyle: {
              normal: {
                borderColor: "#0fc4d890", //图形的描边颜色
                borderWidth: 1.5, //描边线宽。为 0 时无描边。
                borderType: "solid", //柱条的描边类型,默认为实线,支持 'solid', 'dashed', 'dotted'。
                areaColor: "#073051", //图形的颜色 #eee
                label: {
                  show: false,
                  textStyle: {
                    color: "white",
                    fontSize: 14
                  }
                }
              },
              //鼠标移入时
              emphasis: {
                disabled: true,
                borderColor: "#005b89",
                borderWidth: 1,
                areaColor: "#098196",
                label: {
                  color: "#fff"
                }
              },
              effect: {
                show: true,
                shadowBlur: 10,
                loop: true
              }
            },
          },
          {
            type: "scatter",
            coordinateSystem: "geo",
            //自定义图片的 位置(lng, lat)
            data: this.mapData.map((item, index) => {
              if (mapName == "china" || item.parent == this.mapTitle) {
                let symbol = "image://";
                let symbolSize = [];
                let symbolOffset = [];
                switch (item.type) {
                  case "jifang":
                    symbol = symbol + micon_jifang;
                    symbolSize = [40, 50];
                    symbolOffset = [0, -25];
                    break;
                  case "fdevice":
                    symbol = symbol + micon_online;
                    symbolSize = [40, 50];
                    symbolOffset = [0, -25];
                    break;
                  case "odevice":
                    symbol = symbol + micon_offline;
                    symbolSize = [40, 50];
                    symbolOffset = [0, -25];
                    break;
                }
                item.value = [item.lng, item.lat];
                item.symbolSize = symbolSize;
                item.symbolOffset = symbolOffset;
                item.symbol = symbol;
                return item;
              }
            })
          }
        ]
      };
      this.eMap.setOption(this.mapOption);
    },
    backChina () {
      this.initMap("china", china);
      this.mapTitle = "";
    }
  }
};
</script>
<style lang="scss" scoped>
.mapBox {
  width: calc(100% - 40px);
  height: calc(100% - 40px);
  display: flex;
  justify-content: center;
  align-items: center;
  .mapRouter {
    position: absolute;
    top: 50px;
    right: 0px;
    color: #fff;
    font-size: 16px;
    font-weight: 700;
    cursor: pointer;
    .back:hover {
      color: #27fffc;
    }
  }
}
</style>
<style>
.jifang_mapInfo {
  width: 190px;
  height: 238px;
  background: url("./img/jifang_bg.png") no-repeat;
  background-size: 100% 100%;
  margin: 0;
  padding: 12px;
}
.device_mapInfo {
  width: 190px;
  height: 182px;
  background: url("./img/device_bg.png") no-repeat;
  background-size: 100% 100%;
  margin: 0;
  padding: 12px;
}
.mapInfo_title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  height: 27px;
  padding: 0 4px;
  background-image: linear-gradient(
    90deg,
    rgba(68, 253, 251, 0.16) 0%,
    rgba(68, 253, 251, 0.01) 100%
  );
}
.mapInfo_title .title_icon {
  display: inline-block;
  width: 16px;
  height: 17px;
  background: url("./img/panel_icon.png") no-repeat;
  background-size: 100% 100%;
}
.mapInfo_title .text {
  width: 138px;
}
.mapInfo_content {
  width: 100%;
  height: calc(100% - 30px);
}
.mapInfo_content .width8 {
  display: inline-block;
  width: 30%;
  font-size: 12px !important;
  margin-left: 6px;
  margin-top: 8px;
  color: #c5d0d4;
}
.mapInfo_content .width14 {
  display: inline-block;
  width: 70%;
  word-break: keep-all;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: #27fffc;
  vertical-align: bottom;
}
</style>