数据可视化-ECharts

135 阅读8分钟

一、邂逅ECharts

1. 认识ECharts

  • 什么是ECharts
    • ECharts(全称EnterpriseCharts)是企业级数据图表。官方解释是:一个基于JavaScript的开源可视化图表库)。
    • ECharts可以流畅的运行在PC和移动设备上,兼容当前绝大部分浏览器。
    • ECharts底层依赖轻量级的ZRender图形库,可提供直观,生动,可交互,可高度个性化定制的数据可视化图表。
  • ECharts的历史
    • ECharts由百度团队开源
    • 2018年初,捐赠给Apache基金会,成灰Apache软件基金会孵化机项目。
    • 2021年1月26日晚,Apache基金会官方宣布ECharts项目正式毕业,称为Apache顶级项目。
    • 2021年1月28日,ECharts5线上发布会举行。
  • ECharts应用场景
    • 智慧城市、园区、航运、公安、机房、监所、电力、物业、应急管理等多个领域的数据可视化展示。

2. ECharts的特点

  • 丰富的图表类型
    • 提供开箱即用的20多种图表和十几种组件,并且支持各种图表以及组件的任意组合;
  • 强劲的渲染引擎
    • Canvas、SVG双引擎一键切换,增量渲染等技术实现千万级数据的流畅交互;
  • 简单易容,上手容易
    • 直接通过便携配置,便可以生成各种图表,并且支持多种集成方式;
  • 活跃的社区
    • 活跃的社区用户保证了项目的健康发展,也贡献了丰富的第三方插件满足不同场景的需求。

二、ECharts5初体验

1. 集成ECharts的常见方式

  • 通过npm获取echarts
    • npm install echarts --save

2. 初体验ECharts

// 1. 基于准备好的dom,初始化echarts实例
const myChart = echarts.init(document.getElementById("main"))
// 2. 指定图表的配置项和数据
var option = {
    title: {
        text: "ECharts 入门示例"
    },
    tooltip: {},
    legend: {
        data: ["销量"]
    },
    xAxis: {
        data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
    },
    yAxis: {},
    series: [
        {
            name: "销量",
            type: "bar",
            data: [5, 20, 36, 10, 10, 20]
        }
    ]
}
// 3. 使用刚指定的配置项和数据显示图表
myChart.setOption(option)

三、ECharts组件和配置

1.ECharts渲染

  • 渲染原理
    • 浏览器端的图表库大多会选择SVG或者Canvas进行渲染。
    • ECharts最开始时一直都是使用Canvas绘制图表,直到ECharts v4.0版本,才发布SVG渲染器。
    • SVG和Canvas这两种使用方式在技术上是有很大的差异的,EChart能够做到同时支持,主要归功于ECharts底层库ZRender的抽象和实现。
    • ZRender是二维轻量级的绘图引擎,它提供Canvas、SVG、VML等多种渲染方式。
    • 因此,ECharts可以轻松的互换SVG渲染器和Canvas渲染器。切换渲染器只须在初始化图表时设置renderer参数为canvas或svg即可。
  • 渲染器的选择
    • Canvas更适合绘制图形元素较多的图表。
    • SVG具有重要的优势:它的内存占用更低、适配性、扩展性好,放大缩小图表不会模糊。
    • 在软硬件环境较差,出现性能问题需要优化的场景下,可以通过试验来确定使用哪种渲染器。
      • 在需要创建很多ECharts实例且浏览器易崩溃的情况下,可以使用SVG渲染器来进行改善。
      • 数据量较大(经验判断 > 1K)、较多交互时,建议选择Canvas渲染器。

2. option配置项(组件)

  • backgroundColor:设置直角坐标系内绘图区域的背景
  • grid选项:直角坐标系内绘图区域
    • show:是否显示直角坐标系网格。boolean类型。
    • left、right、top、bottom:grid组件离容器左右上下的距离。string|number类型。
    • containLabel:grid区域是否包含坐标轴的刻度标签。boolean类型。
    • backgroundColor:Color类型,网格背景色,默认透明。
  • xAxis选项:直角坐标系grid中的x轴
    • show:是否显示x轴。boolean类型。
    • name:坐标轴名称。
    • type:坐标轴类型。string类型。
      • value 数值轴,适用于连续数据。
      • category 类目轴,适用于离散的类目数据。
    • data:类目数据,在类目轴中有效。array类型。
    • axisLine:坐标轴轴线相关设置。object类型。
    • axiosTick:坐标轴刻度相关设置。object类型。
    • axisLabel:坐标轴刻度标签相关设置。object类型。
    • splitLine:坐标轴在grid区域中的分隔线。object类型。
  • yAxis选项:直角坐标系grid中的y轴
    • 参数与xAxis相似。
  • title:图表的标题
  • legend:图例,展示了不同系列的标记、颜色和名字
  • tooltip:提示框
  • toolbox:工具栏,提供操作图表的工具
  • series:系列,配置系列图表的类型和图形信息数据
    • name:系列名称,用于tooltip的显示,legend的图例筛选等。
    • type:指定系列图表的类型。
    • data:系列中的数值内容数组。数组中的某一项称为数据项。
    • label:图形上的文本标签。
    • itemStyle:图形样式。
    • emphasis:高亮的图形样式和标签样式。
    • coordinateSystem:该系列使用的坐标系,默认值为二维的直角坐标系。
  • visualMap:视觉映射,可以将数据映射到图形的形状、大小、颜色等
  • geo:地理坐标系组件。用于地图的绘制,支持在地图坐标系上绘制散点图,线集。

四、ECharts图表实战

1. 柱形图

var myChart = echarts.init(document.getElementById("main"))

var option = {
    backgroundColor: "rgb(40, 46, 72)",
    grid: {
        left: "5%",
        right: "6%",
        top: "30%",
        bottom: "5%",
        containLabel: true,
    },
    tooltip: {},
    xAxis: {
        name: "",
        axisLine: {
            show: true,
            lineStyle: {
                color: "#42A4FF"
            }
        },
        axisTick: {
            show: false
        },
        axisLabel: {
            color: "white"
        },
        data: ["一月", "二月", "三月", "四月", "五月", "六月", "七月"]
    },
    yAxis: {
      name: "个",
      nameTextStyle: {
        color: "white",
        fontSize: 13,
      },
      axisLine: {
        show: true,
        lineStyle: {
          color: "#42A4FF",
        },
      },
      axisTick: {
        show: false,
      },
      splitLine: {
        show: true,
        lineStyle: {
          color: "#42A4FF",
        },
      },
      axisLabel: {
        color: "white",
      },
    },
    series: [
      {
        name: "销量",
        type: "bar",
        barWidth: 17,
        itemStyle: {
          color: {
            type: "linear",
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              {
                offset: 0,
                color: "#01B1FF", // 0% 处的颜色
              },
              {
                offset: 1,
                color: "#033BFF", // 100% 处的颜色
              },
            ],
            global: false, // 缺省为 false
          },
        },
        data: [500, 2000, 3600, 1000, 1000, 2000, 4000],
      },
    ]
}

myChart.setOption(option);

2. 折线图

var myChart = echarts.init(document.getElementById("main"))

var option = {
    backgroundColor: "rbg(40,46,72)",
    grid: {
      left: "5%",
      right: "1%",
      top: "20%",
      bottom: "15%",
      containLabel: true, // grid 区域是否包含坐标轴的刻度标签
    },
    legend: {
      bottom: "5%",
      itemGap: 20,
      itemWidth: 13,
      itemHeigth: 12,
      textStyle: {
        color: "#64BCFF",
      },
      icon: "rect",
    },
    tooltip: {
      trigger: "axis",
      axisPointer: {
        type: "line",
        lineStyle: {
          color: "#20FF89",
        },
      },
    },
    xAxis: [
      {
        type: "category",
        axisLine: {
          show: false,
        },
        axisLabel: {
          color: "#64BCFF",
        },
        splitLine: {
          show: false,
        },
        axisTick: {
          show: false,
        },
        data: [
          "1月",
          "2月",
          "3月",
          "4月",
          "5月",
          "6月",
          "7月",
          "8月",
          "9月",
          "10月",
          "11月",
          "12月",
        ],
      },
    ],
    yAxis: [
      {
        type: "value",
        splitLine: {
          show: false,
        },
        axisLine: {
          show: false,
        },
        axisLabel: {
          show: true,
          color: "#64BCFF",
        },
      },
    ],
    series: [
      // 第一条折线图
      {
        name: "正常",
        type: "line",
        smooth: true,  // 是否平滑曲线显示。
        symbolSize: 5, // 标记的大小
        showSymbol: false,
        itemStyle: {
          color: "#20FF89",
        },
        // 区域填充样式。设置后显示成区域面积图。
        areaStyle: {
          color: new echarts.graphic.LinearGradient(
            0,
            0,
            0,
            1,
            [
              {
                offset: 0,
                color: "#20FF89",
              },
              {
                offset: 1,
                color: "rgba(255, 255, 255, 0)",
              },
            ],
            false
          ),
        },
        data: [200, 200, 191, 234, 290, 330, 310, 201, 154, 190, 330, 410],
      },
      // 第二条折线图
      {
        name: "异常",
        type: "line",
        smooth: true, // 是否平滑曲线显示。
        symbolSize: 5, // 标记的大小,可以设置成诸如 10 这样单一的数字
        showSymbol: false, // 是否显示 symbol, 如果 false 则只有在 tooltip hover 的时候显示。
        itemStyle: {
          // 折线的颜色
          color: "#EA9502",
        },
        // 折线区域的颜色
        areaStyle: {
          color: {
            type: "linear",
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              {
                offset: 0,
                color: "#EA9502",
              },
              {
                offset: 1,
                color: "rgba(255, 255, 255, 0)",
              },
            ],
          },
        },
        data: [500, 300, 202, 258, 280, 660, 320, 202, 308, 280, 660, 420],
      },
    ],
}; 

myChart.setOption(option);

3. 饼图

  var myChart = echarts.init(document.getElementById("main"))

  // =====准备数据=====
  let pieDatas = [
    {
      value: 100,
      name: "广州占比",
      percentage: "5%",
      color: "#34D160",
    },
    {
      value: 200,
      name: "深圳占比",
      percentage: "4%",
      color: "#027FF2",
    },
    {
      value: 300,
      name: "东莞占比",
      percentage: "8%",
      color: "#8A00E1",
    },
    {
      value: 400,
      name: "佛山占比",
      percentage: "10%",
      color: "#F19610",
    },
    {
      value: 500,
      name: "中山占比",
      percentage: "20%",
      color: "#6054FF",
    },
    {
      value: 600,
      name: "珠海占比",
      percentage: "40%",
      color: "#00C6FF",
    },
  ];

  // 将 pieDatas 格式的 数据映射为 系列图所需要的数据格式
  var data = pieDatas.map((item) => {
    return {
      value: item.value,
      name: item.name,
      itemStyle: {
        color: item.color,
      },
    };
  });

  // 求出总数
  let total = pieDatas.reduce((a, b) => {
    return a + b.value * 1;
  }, 0);
  // =====准备数据=====

  // 2.指定图表的配置项和数据
  var option = {
    backgroundColor: "rbg(40,46,72)",
    title: {
      text: `充电桩总数`,
      top: "50%",
      left: "50%",
      padding: [-20, 0, 0, -45],
      textStyle: {
        fontSize: 19,
        color: "white",
      },

      // 副标题使用-富文本语法:{style_name|value}, 注意不能有空格
      subtext: `{totalSty|${total}}`,
      subtextStyle: {
        rich: {
          totalSty: {
            fontSize: 19,
            color: "white",
            width: 90,
            align: "center",
          },
        },
      },
    },
    legend: {
      orient: "vertical",
      right: "10%",
      top: "18%",
      itemGap: 20,
      itemWidth: 16,
      itemHeigth: 16,
      icon: "rect",
      // 自定义图例的名称
      formatter: function (name) {
        // 图例文本格式化 + 富文本定制样式  
        var currentItem = pieDatas.find((item) => item.name === name);
        return (
          "{nameSty|" +
          currentItem.name +
          "}\n" +
          "{numberSty|" +
          currentItem.value +
          "个 }" +
          "{preSty|" +
          currentItem.percentage +
          "}"
        );
      },
      textStyle: {
        rich: {
          nameSty: {
            fontSize: 12,
            color: "#FFFFFF",
            padding: [10, 14],
          },
          numberSty: {
            fontSize: 12,
            color: "#40E6ff",
            padding: [0, 0, 0, 14],
          },
          preSty: {
            fontSize: 12,
            color: "#40E6ff",
          },
        },
      },
    },
    series: [
      {
        type: "pie",
        center: ["50%", "50%"], // 饼图的中心(圆心)坐标,数组的第一项是横坐标,第二项是纵坐标。
        radius: ["30%", "75%"], // 饼图的半径。数组的第一项是内半径,第二项是外半径。
        label: {
          show: false,
        },
        // data: [  { name: '',   value: '',   itemStyle }  ],
        data: data,
        roseType: "area", //  area 玫瑰图, 圆心角一样,通过半径展现数据大小( 默认为false )
      },
    ],
  };

  myChart.setOption(option);

4. 地图

  • 绘制地图的两种方式
    • 方式一
    // 1. 拿到GeoJson数据
    // 2. 注册对应地图的geojson数据(需要在setOption之前调用)
    echarts.registerMap("中国", { geoJSON: china_geojson })
    var myChart = echarts.init(document.getElementById("main"))
    // 3. 在echarts中展示中国地图(配置geo选项)
    var option = {
       geo: {
          map: "china"
       }
    };
    myChart.setOption(option);
    
    • 方式二
    // 1. 拿到GeoJson数据
    // 2. 注册对应地图的geojson数据(需要在setOption之前调用)
    var myChart = echarts.init(document.getElementById("main"))
    // 3. 在echarts中展示中国地图(配置map series选项)
    var option = {
       series: [
           {
               type: "map", // 系列图的类型 地图
               map: "中国" // 展示中国地图(因为只注册一个中国地图)
           }
       ]
    };
    myChart.setOption(option);
    
    • geo地图和series地图区别
      • geo会生成一个geo地理坐标系组件,支持在地理坐标系上绘制散点图、线集,可以供其他系列复用。
      • series会生成自己内部专用的geo地理坐标系组件,地图主要用于地理区域数据的可视化,配合data使用。
  • 地图-着色
echarts.registerMap("中国", { geoJSON: china_geojson })
var myChart = echarts.init(document.getElementById("main"))

var option = {
    geo: {
        map: "中国",
        roam: false, // 是否开启鼠标缩放和平移漫游,默认不开启
        label: {
            // 图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等
            show: false
        },
        aspectScale: 0.75, // 这个参数用于 scale 地图的长宽比,如果设置了projection则无效
        // =======地图着色=========
        itemStyle: {
            areaColor: "#023677", // 地图区域的颜色。
            borderColor: "#1180c7", // 图形的描边颜色。
        },
         
        emphasis: {
            itemStyle: {
              areaColor: "#4499d0",
            },
            label: {
              color: "white",
            },
        }
    }
}

myChart.setOption(option);
  • 地图-填充数据
echarts.registerMap("中国", { geoJSON: china_geojson })
var myChart = echarts.init(document.getElementById("main"))

var data = [
        { name: "北京", value: 199 },
        { name: "天津", value: 42 },
        { name: "河北", value: 102 },
        { name: "山西", value: 81 },
        { name: "内蒙古", value: 47 },
        { name: "辽宁", value: 67 },
        { name: "吉林", value: 82 },
        { name: "黑龙江", value: 123 },
        { name: "上海", value: 24 },
        { name: "江苏", value: 92 },
        { name: "浙江", value: 114 },
        { name: "安徽", value: 109 },
        { name: "福建", value: 116 },
        { name: "江西", value: 91 },
        { name: "山东", value: 119 },
        { name: "河南", value: 137 },
        { name: "湖北", value: 116 },
        { name: "湖南", value: 114 },
        { name: "重庆", value: 91 },
        { name: "四川", value: 125 },
        { name: "贵州", value: 62 },
        { name: "云南", value: 83 },
        { name: "西藏", value: 9 },
        { name: "陕西", value: 80 },
        { name: "甘肃", value: 56 },
        { name: "青海", value: 10 },
        { name: "宁夏", value: 18 },
        { name: "新疆", value: 180 },
        { name: "广东", value: 123 },
        { name: "广西", value: 59 },
        { name: "海南", value: 14 },
      ];

var option = {
    tooltip: {},
    grid: {},
     // 视觉数据映射
     visualMap: [
      {
        // type: "continuous", // 连续型视觉映射组件 (默认)
        // type: "piecewise", // 分段型视觉映射组件
        left: "20%",
        seriesIndex: [0], // 指定取哪个系列的数据
        // 定义 在选中范围中 的视觉元素, 对象类型。
        inRange: {
          color: ["#04387b", "#467bc0"], // 映射组件和地图的颜色(一般和地图色相近)
        },
      },
    ],
    series: [{
        name: "中国地图",
        type: "map",
        map: "中国",
        // =======地图着色=========
        itemStyle: {
            areaColor: "#023677", // 地图区域的颜色。
            borderColor: "#1180c7", // 图形的描边颜色。
        },
        emphasis: {
            itemStyle: {
              areaColor: "#4499d0",
            },
            label: {
              color: "white",
            },
        },
        select: {
              label: { color: "white" },
              itemStyle: { areaColor: "#4499d0" },
        },
        // ===== 添加数据(不需要地理坐标点,直接使用name) =====
        // name: 数据所对应的地图区域的名称,例如 '广东','浙江'。  
        // value: 该区域的数据值。。  
        // data: [ {name:'' ,  value: '' }, .... ]  
        data
    }]
}

myChart.setOption(option);
  • 地图-散点图
echarts.registerMap("中国", { geoJSON: china_geojson })
var myChart = echarts.init(document.getElementById("main"))

var mapName = "中国"
var data = [
    { name: "北京", value: 199 },
    { name: "天津", value: 42 },
    { name: "河北", value: 102 },
    { name: "山西", value: 81 },
    { name: "内蒙古", value: 47 },
    { name: "辽宁", value: 67 },
    { name: "吉林", value: 82 },
    { name: "黑龙江", value: 123 },
    { name: "上海", value: 154 },
    { name: "江苏", value: 102 },
    { name: "浙江", value: 114 },
    { name: "安徽", value: 109 },
    { name: "福建", value: 116 },
    { name: "江西", value: 91 },
    { name: "山东", value: 119 },
    { name: "河南", value: 137 },
    { name: "湖北", value: 116 },
    { name: "湖南", value: 114 },
    { name: "重庆", value: 101 },
    { name: "四川", value: 125 },
    { name: "贵州", value: 62 },
    { name: "云南", value: 83 },
    { name: "西藏", value: 9 },
    { name: "陕西", value: 80 },
    { name: "甘肃", value: 56 },
    { name: "青海", value: 10 },
    { name: "宁夏", value: 18 },
    { name: "新疆", value: 120 },
    { name: "广东", value: 193 },
    { name: "广西", value: 59 },
    { name: "海南", value: 14 },
];

var geoCoordMap = {};
/*获取地图数据*/
myChart.showLoading();
var mapFeatures = echarts.getMap(mapName).geoJson.features;
mapFeatures.forEach(function (v) {
    // 地区名称
    var name = v.properties.name;
    // 地区经纬度
    geoCoordMap[name] = v.properties.cp;
});
myChart.hideLoading();
console.log("data=>", data);
console.log("geoCoordMap=>", geoCoordMap);

var convertData = function (data) {
    var res = [];
    for (var i = 0; i < data.length; i++) {
      var geoCoord = geoCoordMap[data[i].name];
      if (geoCoord) {
        res.push({
          name: data[i].name,
          value: [...geoCoord, data[i].value],
        });
      }
    }
    console.log("res=>", res);
    return res;
};

// 指定图表的配置项和数据
var option = {
    tooltip: {}, // 提示框
    visualMap: { // 视觉映射组件
      left: "20%",
      seriesIndex: [0],
      inRange: {
        color: ["#04387b", "#467bc0"], // 蓝绿
      },
    },
    geo: { // 注册一个地理坐标系组件(给散点图用)
      map: "中国",
      roam: false,
      label: { show: false },
      aspectScale: 0.75, // 缩放地图
      itemStyle: {
        areaColor: "#023677",
        borderColor: "#1180c7",
      },
      emphasis: {
        itemStyle: { areaColor: "#4499d0" },
        label: { color: "white" },
      },
    },
    series: [
      {
        name: "中国地图",
        type: "map",
        map: "中国",
        data, // 给地图填充数据
        // 地图样式
        itemStyle: {
          areaColor: "#023677",
          borderColor: "#1180c7",
        },
        emphasis: {
          itemStyle: { areaColor: "#4499d0" },
          label: { color: "white" },
        },
        select: {
          label: { color: "white" },
          itemStyle: { areaColor: "#4499d0" },
        },
        // 地图样式
      },

      {
        name: "散点图充电桩",
        type: "effectScatter",
        // 散点图使用的坐标系:geo定义的坐标系组件
        geoIndex: 0,
        coordinateSystem: "geo", // 使用地理坐标系,通过 geoIndex 指定相应的地理坐标系组件。
        data: convertData(data),
        symbolSize: function (val) {
          return val[2] / 10;
        },
        itemStyle: {
          color: "yellow",
          shadowBlur: 10,
          shadowColor: "yellow",
        },
        tooltip: {
          show: true,
          trigger: "item",
          formatter: function (params) {
            console.log(params);
            var data = params.data;
            return `${params.seriesName} <div style="margin:5px 0px;"/> ${data.name} ${data.value[2]}`;
          },
        },
      },
   ],
};

myChart.setOption(option);

五、ECharts其他补充

1. ECharts常见API

  • 全局echarts对象
    • echarts.init(dom, theme, opts):创建echartsInstance实例
    • echarts.registerMap(mapName, opts):注册地图
    • echarts.getMap(mapName):获取已注册地图
  • 通过echarts.init创建的实例(echartsInstance)
    • echartsInstance.setOption(opts):设置图表实例的配置项以及数据,万能接口
    • echartsInstance.getWidth/getHeight():获取ECharts实例容器的宽高度
    • echartsInstance.resize(opts):改变图表尺寸,在容器大小发生改变时需要手动调用
    • echartsInstance.showLoading/hideLoading():显示和隐藏加载动画效果
    • echartsInstance.dispatchAction():触发图表行为
    • echartsInstance.dispose:销毁实例,销毁后实例无法再被使用
    • echartsInstance.on():通过on方法添加事件处理函数

2. 响应式ECharts图表

  • 响应式图表的实现步骤
    • 图表只设置高度,宽度设置为100%或者不设置;
    • 监听窗口的resize事件,即监听窗口尺寸的变化(需节流);
    • 当窗口大小发生改变时,然后调用echartsInstance.resize改变图表的大小。
window.addEventListener("resize", function () {
    myChart.resize()
})