写在前面

在这里插入图片描述

  • 本文要介绍的是如何使用echarts来实现上图的可视化看板栗子,接下来我将以分步骤的方式将此功能逐步实现(希望足够细节…);
  • 技术栈主要是 Echarts4 + vue2
  • 在这之前我有写过一个Vue3+Ts+Websocket+Echarts5+Nodejs的完整栗子,这个例子除了地图还包括了一些常用的图表,例如饼图,折线图啥的;感兴趣的也可以看看,仓库地址传送门

1.准备一些静态资源

  • 首要的就是地图的json或是js数据(没有的话可以进仓库复制一下,我放在了public下面的static文件夹里,你只需拷贝china.json即可);
  • 可以根据自己喜好找一些背景图啥的,结合样式设置,可以让你的页面看起来更加酷炫;

2.页面布局

  • 根据效果图,这里页面大体分为上下两部分,分为头部栏目和内容区域;
  • 布局样式采用的 flex 布局;
  • 相关的样式布局就不描述了,直接给代码,这里只介绍echarts相关的js逻辑代码。

3.渲染地图数据

3.1给一个容器并设置宽高

  • 首先我们需要一个容器,这里给了一个div,类名就叫map;
  • 宽高的话100%,也就是占满整个父容器;
    在这里插入图片描述

3.2引入地图的json数据并渲染

  • 通过import的方式导入json数据
  • 接着在 mounted 函数中调用 echarts实例的 registerMap方法进行注册
  • 而后自定义一些配置参数,并且通过 init 函数初始化地图
  • 这里我通过 this.$echarts.xxx方法的原因是我在入口文件main.js中把echarts实例挂载到了Vue的原型对象上,所以这么引用;
    在这里插入图片描述
  • 如果你没有这么做的话,可以在当前文件直接引入echarts,就像下面这样:
    在这里插入图片描述
  • 最后使用简单的三步就可以把地图渲染至页面了
    在这里插入图片描述

3.3页面效果以及代码

在这里插入图片描述

  • 代码
<template>
  <div class="wrap">
    <header class="header">
      <div class="bg">
        <img src="../../assets/imgs/header_border_dark.png" alt="" />
      </div>
      <div class="title">北极星的看板栗子</div>
      <div class="time">
        <span>当前时间:</span>
        <span> 2024年1月15日 9:50 </span>
      </div>
    </header>
    <div class="body">
      <section class="left">
        <div class="title">▣ 选择地区</div>
        <div class="map" ref="mapRef"></div>
      </section>
      <section class="right">
        <div class="title">
          <span class="desc"
            >各种动物数量排名列表 <span class="order"> &nbsp;&nbsp;TOP10</span></span
                                                                       >
        </div>
        <div class="list">
        </div>
      </section>
    </div>
  </div>
</template>
<script>
  import chinaJson from '/public/static/map/china.json';
  import cloneDeep from 'lodash/cloneDeep';
  export default {
    data() {
      return {
        chartRef: null,
    };
  },
  mounted() {
    this.$echarts.registerMap('china', chinaJson);
    this.initChart();
  },
  methods: {
    initChart() {
      const option = {
        tooltip: {
          //提示框组件
          show: true,
          trigger: 'item'
        },
        series: [
          {
            name: '中国',
            type: 'map',
            map: 'china',
            left: '10%', //移动地图在容器中的位置
            top: '8%',
            label: {
              show: true,
              color: '#fff',
              fontSize: 10
            },
            itemStyle: {
              areaColor: '#151d4c',
              borderWidth: 1, // 区域边框宽度,默认为1,表示边框宽度为1px
              borderColor: '#000' // 区域边框颜色值,默认为#e1e1e1,表示浅灰色边框
            },
            emphasis: {
              // 选中状态下的样式设置
              label: {
                // 标签样式设置
                show: true
              },
              itemStyle: {
                // 区域填充样式设置
                areaColor: '#90c31d'
              }
            },
          }
        ]
      };
      this.chartRef = this.$echarts.init(this.$refs.mapRef);
      this.chartRef.setOption(option);
    }
  }
};
</script>
<style lang="less" scoped>
.wrap {
  width: 100%;
  height: 100%;
  background-color: #152f5b;
  .header {
    width: 100%;
    height: 12%;
    // background-color: rgb(34, 32, 30);
    position: relative;
    .bg {
      width: 100%;
      position: absolute;
      img {
        width: 100%;
      }
    }
    .title {
      position: absolute;
      left: 50%;
      transform: translateX(-50%);
      color: #fff;
      font-size: 20px;
      top: 15%;
    }
    .time {
      position: absolute;
      left: 2%;
      bottom: 0;
      color: #fff;
      font-size: 20px;
    }
  }
  .body {
    width: 100%;
    height: 87%;
    display: flex;
    .left {
      width: 59%;
      margin-right: 10px;
      height: 100%;
      border: 1px salmon solid;
      // background-color: rgb(136, 182, 182);
      position: relative;
      .title {
        position: absolute;
        width: 80%;
        height: 50px;
        line-height: 50px;
        color: #fff;
        padding-left: 20px;
      }
      .map {
        height: 100%;
        width: 100%;
      }
    }
    .right {
      width: 40%;
      height: 100%;
      border: 1px #ff9f43 solid;
      .title {
        width: 60%;
        height: 50px;
        line-height: 50px;
        // border: 1px solid seagreen;
        // padding-left: 20px;
        color: #ff9f43;
        font-weight: 600;
        .desc {
          text-shadow: 2px 2px 4px #ff9f43;
          text-decoration: underline #2ccedf 2px;
          text-decoration-skip: none;
        }
      }
      .list {
        color: #fff;
        background-color: #0a06069e;
        width: 100%;
      }
    }
  }
}
</style>

4.自定义不同区块颜色

  • 在上面的步骤我们已经把地图渲染出来了,但是每个区块的颜色完全一样
  • 为了美观我们可以自行定义每个区块的颜色
  • 这里我们通过设置 visualMap参数来实现,具体配置在下面截图说明,如下:
    在这里插入图片描述
  • 这时候再看页面的效果
    在这里插入图片描述
  • 代码
data() {
  return {
    // 新增省区数据源
    provinceList: [
      { name: '吉林', value: 36, pos: [125.8154, 44.2584] },
      { name: '北京', value: 32, pos: [116.4551, 40.2539] },
      { name: '辽宁', value: 54, pos: [123.1238, 42.1216] },
      { name: '河北', value: 23, pos: [114.4995, 38.1006] },
      { name: '天津', value: 78, pos: [117.4219, 39.4189] },
      { name: '山西', value: 30, pos: [112.3352, 37.9413] },
      { name: '陕西', value: 50, pos: [109.1162, 34.2004] },
      { name: '甘肃', value: 80, pos: [103.5901, 36.3043] },
      { name: '宁夏', value: 20, pos: [106.3586, 38.1775] },
      { name: '四川', value: 60, pos: [103.9526, 30.7617] },
      { name: '山东', value: 30, pos: [117.1582, 36.8701] },
      { name: '河南', value: 24, pos: [113.4668, 34.6234] },
      { name: '江苏', value: 72, pos: [118.8062, 31.9208] },
      { name: '湖北', value: 90, pos: [114.3896, 30.6628] },
      { name: '浙江', value: 30, pos: [119.5313, 29.8773] },
      { name: '福建', value: 40, pos: [119.4543, 25.9222] },
      { name: '江西', value: 20, pos: [116.0046, 28.6633] },
      { name: '湖南', value: 80, pos: [113.0823, 28.2568] },
      { name: '贵州', value: 30, pos: [106.6992, 26.7682] },
      { name: '云南', value: 10, pos: [102.9199, 25.4663] },
      { name: '海南', value: 20, pos: [110.3893, 19.8516] },
      { name: '上海', value: 82, pos: [121.4648, 31.2891] },
      { name: '香港', value: 60, pos: [114.3, 22.9] },
      { name: '澳门', value: 30, pos: [113.5, 22.2] },
      { name: '台湾', value: 70, pos: [121, 23] },
      { name: '广东', value: 85, pos: [113.12244, 23.009505] },
      { name: '安徽', value: 59, pos: [117.29, 32.0581] },
      { name: '广西', value: 70, pos: [108.479, 23.1152] },
      { name: '青海', value: 30, pos: [99.4038, 36.8207] },
      { name: '新疆', value: 30, pos: [87.9236, 43.5883] },
      { name: '西藏', value: 80, pos: [88.388277, 31.56375] },
      { name: '重庆', value: 70, pos: [108.384366, 30.439702] },
      { name: '黑龙江', value: 70, pos: [127, 48] },
      { name: '内蒙古', value: 30, pos: [110.3467, 41.4899] }
    ],
  };
},
methods: {
  // 修改初始化图表函数
  initChart() {
      const option = {
        // 新增此配置
        visualMap: {
          textStyle: {
            color: '#fff'
          },
          show: true, // 是否显示视觉映射组件
          min: 0, // 视觉映射组件的最小值
          max: 100, // 视觉映射组件的最大值
          left: '2%', // 视觉映射组件的左边界
          bottom: '12%', // 视觉映射组件的上边界
          calculable: true, // 是否开启视觉映射组件的拖拽缩放功能
          inRange: {
            // 视觉映射组件的色彩映射区间
            // 不同区间的颜色值
            color: [
              '#4c78da',
              '#514aca',
              '#4243c2',
              '#4d32a5',
              '#3892dc',
              '#5f1e83',
              '#25abde',
              '#3497dc',
              '#dfc32d'
            ]
          }
        },
        series: [
          {
            // 新增此属性
            data: this.provinceList
          }
        ]
      };
}

5.高亮选中的区域

  • 在单击省区的时候希望高亮此区块,一是能够保留上次选中的效果,二是后续可以通过此操作获取不同省区的数据,例如点击之后调用接口获取不同数据展示在右侧;
  • 这里核心方法是通过 echarts实例上的on方法去监听点击操作;
  • 新增一个linstenProvinceClick函数,在点击之后将该省份的value值设置为100 (100在当前栗子中对应的就是黄色),而后通过 setOption 方法重新渲染图表;
  • setOption这个方法类似于 Object.assign方法,他会将配置进行合并而不是覆盖,并且允许多次调用;
    在这里插入图片描述
  • 代码
linstenProvinceClick() {
  // 接收一个对象, 结构出的data对象值为series数组中data数据源中的对象
  this.chartRef.on('click', ({ data }) => {
    const provinceName = data.name;
    // cloneDeep 是lodash 库的方法,深拷贝一份数组,避免对原始数据造成影响
    const provinceList = cloneDeep(this.provinceList);
    provinceList?.forEach(item => {
      if (item.name === provinceName) {
        item.value = 100;
      }
    });
    const option = {
      series: [
        {
          data: provinceList
        }
      ]
    };
    this.chartRef.setOption(option);
  });
}
  • 这时候回到页面可以看到,点击过的区域变成了黄色,绿色是原本设置的鼠标滑过的颜色;
    在这里插入图片描述

6.根据页面窗口变化使图表自适应

● echarts实例提供 resize方法,调用时可以改变图表的尺寸
● 在组件中监听窗口的变化调用 resize方法
在这里插入图片描述

  • 在浏览器看看效果,可以发现当窗口缩小时,图表也自动缩放了
    在这里插入图片描述
  • 代码
beforeDestroy() {
  // 组件销毁前取消监听事件
  window.removeEventListener(this.adaptScreen);
},
methods: {
  initChart() {
    // 新增监听
    window.addEventListener('resize', this.adaptScreen);
  },
  adaptScreen() {
    this.chartRef.resize();
  }
}

7.右侧添加列表数据展示看板

● 这个根据自己需求是否添加;
● 表格组件这里没有使用element-ui的 el-table 组件,而是使用原生的table,这样方便自行定义样式;否则需要使用 属性透传的方式去修改el-table的默认样式,视自己情况而定;
● 最终效果就如下
在这里插入图片描述

8.最终代码

<template>
  <div class="wrap">
    <header class="header">
      <div class="bg">
        <img src="../../assets/imgs/header_border_dark.png" alt="" />
      </div>
      <div class="title">北极星的看板栗子</div>
      <div class="time">
        <span>当前时间:</span>
        <span> 2024年1月15日 9:50 </span>
      </div>
    </header>
    <div class="body">
      <section class="left">
        <div class="title">▣ 选择地区</div>
        <div class="map" ref="mapRef"></div>
      </section>
      <section class="right">
        <div class="title">
          <span class="desc"
            >各种动物数量排名列表 <span class="order"> &nbsp;&nbsp;TOP10</span></span
          >
        </div>
        <div class="list">
          <table class="table" cellspacing="0" border="1">
            <thead>
              <tr>
                <th width="10%">排名</th>
                <th width="15%">动物名称</th>
                <th width="15%">来自地区</th>
                <th width="23%">增长率</th>
                <th width="15%">总数量</th>
                <th width="22%">死亡率</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="center">1</td>
                <td align="center">雄狮院长</td>
                <td align="center">大草原</td>
                <td align="center">20%</td>
                <td align="center">18895</td>
                <td align="center">15%</td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>
    </div>
  </div>
</template>
<script>
import chinaJson from '/public/static/map/china.json';
import cloneDeep from 'lodash/cloneDeep'; //lodash库中深拷贝对象的方法
export default {
  data() {
    return {
      chartRef: null,//echarts初始化的实例对象
      provinceList: [
        { name: '吉林', value: 36, pos: [125.8154, 44.2584] },
        { name: '北京', value: 32, pos: [116.4551, 40.2539] },
        { name: '辽宁', value: 54, pos: [123.1238, 42.1216] },
        { name: '河北', value: 23, pos: [114.4995, 38.1006] },
        { name: '天津', value: 78, pos: [117.4219, 39.4189] },
        { name: '山西', value: 30, pos: [112.3352, 37.9413] },
        { name: '陕西', value: 50, pos: [109.1162, 34.2004] },
        { name: '甘肃', value: 80, pos: [103.5901, 36.3043] },
        { name: '宁夏', value: 20, pos: [106.3586, 38.1775] },
        { name: '四川', value: 60, pos: [103.9526, 30.7617] },
        { name: '山东', value: 30, pos: [117.1582, 36.8701] },
        { name: '河南', value: 24, pos: [113.4668, 34.6234] },
        { name: '江苏', value: 72, pos: [118.8062, 31.9208] },
        { name: '湖北', value: 90, pos: [114.3896, 30.6628] },
        { name: '浙江', value: 30, pos: [119.5313, 29.8773] },
        { name: '福建', value: 40, pos: [119.4543, 25.9222] },
        { name: '江西', value: 20, pos: [116.0046, 28.6633] },
        { name: '湖南', value: 80, pos: [113.0823, 28.2568] },
        { name: '贵州', value: 30, pos: [106.6992, 26.7682] },
        { name: '云南', value: 10, pos: [102.9199, 25.4663] },
        { name: '海南', value: 20, pos: [110.3893, 19.8516] },
        { name: '上海', value: 82, pos: [121.4648, 31.2891] },
        { name: '香港', value: 60, pos: [114.3, 22.9] },
        { name: '澳门', value: 30, pos: [113.5, 22.2] },
        { name: '台湾', value: 70, pos: [121, 23] },
        { name: '广东', value: 85, pos: [113.12244, 23.009505] },
        { name: '安徽', value: 59, pos: [117.29, 32.0581] },
        { name: '广西', value: 70, pos: [108.479, 23.1152] },
        { name: '青海', value: 30, pos: [99.4038, 36.8207] },
        { name: '新疆', value: 30, pos: [87.9236, 43.5883] },
        { name: '西藏', value: 80, pos: [88.388277, 31.56375] },
        { name: '重庆', value: 70, pos: [108.384366, 30.439702] },
        { name: '黑龙江', value: 70, pos: [127, 48] },
        { name: '内蒙古', value: 30, pos: [110.3467, 41.4899] }
      ]
    };
  },
  mounted() {
    // 名称必须是 china ,否则南海岛屿无法展示
    this.$echarts.registerMap('china', chinaJson);
    this.initChart();
  },
  beforeDestroy() {
    window.removeEventListener(this.adaptScreen);
  },
  methods: {
    initChart() {
      const option = {
        tooltip: {
          //提示框组件
          show: true,
          trigger: 'item'
        },
        visualMap: {
          textStyle: {
            color: '#fff'
          },
          show: true, // 是否显示视觉映射组件
          min: 0, // 视觉映射组件的最小值
          max: 100, // 视觉映射组件的最大值
          left: '2%', // 视觉映射组件的左边界
          bottom: '12%', // 视觉映射组件的上边界
          calculable: true, // 是否开启视觉映射组件的拖拽缩放功能
          inRange: {
            // 视觉映射组件的色彩映射区间
            // 不同区间的颜色值
            color: [
              '#4c78da',
              '#514aca',
              '#4243c2',
              '#4d32a5',
              '#3892dc',
              '#5f1e83',
              '#25abde',
              '#3497dc',
              '#dfc32d'
            ]
          }
        },
        series: [
          {
            name: '中国',
            type: 'map',
            map: 'china',
            left: '10%', //移动地图在容器中的位置
            top: '8%',
            label: {
              show: true,
              color: '#fff',
              fontSize: 10
            },
            itemStyle: {
              areaColor: '#151d4c',
              borderWidth: 1, // 区域边框宽度,默认为1,表示边框宽度为1px
              borderColor: '#000' // 区域边框颜色值,默认为#e1e1e1,表示浅灰色边框
            },
            emphasis: {
              // 选中状态下的样式设置
              label: {
                // 标签样式设置
                show: true
              },
              itemStyle: {
                // 区域填充样式设置
                areaColor: '#90c31d'
              }
            },
            data: this.provinceList
          }
        ]
      };
      this.chartRef = this.$echarts.init(this.$refs.mapRef);
      this.linstenProvinceClick();
      this.chartRef.setOption(option);
      window.addEventListener('resize', this.adaptScreen);
    },
    adaptScreen() {
      this.chartRef.resize();
    },
    linstenProvinceClick() {
      // 接收一个对象, 解构出的data对象值为series数组中data数据源中的对象
      this.chartRef.on('click', ({ data }) => {
        if (!data || !data.name) return;
        const provinceName = data.name;
        const provinceList = cloneDeep(this.provinceList);
        provinceList?.forEach(item => {
          if (item.name === provinceName) {
            item.value = 100;
          }
        });
        const option = {
          series: [
            {
              data: provinceList
            }
          ]
        };
        this.chartRef.setOption(option);
      });
    }
  }
};
</script>
<style lang="less" scoped>
.wrap {
  width: 100%;
  height: 100%;
  background-color: #152f5b;
  .header {
    width: 100%;
    height: 12%;
    // background-color: rgb(34, 32, 30);
    position: relative;
    .bg {
      width: 100%;
      position: absolute;
      img {
        width: 100%;
      }
    }
    .title {
      position: absolute;
      left: 50%;
      transform: translateX(-50%);
      color: #fff;
      font-size: 20px;
      top: 15%;
    }
    .time {
      position: absolute;
      left: 2%;
      bottom: 0;
      color: #fff;
      font-size: 20px;
    }
  }
  .body {
    width: 100%;
    height: 87%;
    display: flex;
    .left {
      width: 59%;
      margin-right: 10px;
      height: 100%;
      position: relative;
      .title {
        position: absolute;
        width: 80%;
        height: 50px;
        line-height: 50px;
        color: #fff;
        padding-left: 20px;
      }
      .map {
        height: 100%;
        width: 100%;
      }
    }
    .right {
      width: 40%;
      height: 100%;
      .title {
        width: 60%;
        height: 50px;
        line-height: 50px;
        color: #ff9f43;
        font-weight: 600;
        .desc {
          text-shadow: 2px 2px 4px #ff9f43;
          text-decoration: underline #2ccedf 2px;
          text-decoration-skip: none;
        }
      }
      .list {
        color: #fff;
        background-color: #0a06069e;
        width: 100%;
        table {
          width: 100%;
          thead {
            th {
              font-weight: normal;
              height: 30px;
            }
          }
          td {
            border-top: 1px solid #2ccedf;
            height: 25px;
          }
        }
      }
    }
  }
}
</style>

写在最后

  • 以上就是本文的全部内容了,希望这篇文章能对你在使用echarts渲染地图的功能实现上有所帮助;
  • 另外,如果本文对你有所帮助或者有所启发的话,可以点个赞鼓励一下,感谢你这么优秀还能看我的文章;
  • 仓库地址放这了,传送门~~
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐