# Echarts使用

Echarts官网:http://echarts.apache.org/zh/index.html (opens new window)

option配置项:https://echarts.apache.org/zh/option.html (opens new window)

图表社区:makeapie (opens new window)isqqw (opens new window)

Echarts通过canvas来渲染,指定一个id容器,通过option指定不同的参数来实现。option最常用的配置项就有 title、tooltip、legend、xAxis、yAxis、series等,series是图表内容配置,通过维护该数组可以在一个图表上设置多个条数据,比如嵌套饼状图,多条折线图。

# 快速使用

# 1、在HTML中引入

在HTML中简单使用Echarts直接通过cdn地址引入就可以了。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>echarts</title>

  <body>
    <!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
    <div id="main" style="width: 600px;height:400px;"></div>

    <!-- 引入echarts -->
    <script src="https://cdn.jsdelivr.net/npm/echarts@4.9.0/dist/echarts.min.js"></script>
    <script type="text/javascript">
      // 基于准备好的dom,初始化echarts实例
      var myChart = echarts.init(document.getElementById('main'));

      // 指定图表的配置项和数据
      var option = {
          // 标题
          title: {
            text: 'ECharts 入门示例'
          },
          // 鼠标放上去提示框
          tooltip: {},
          // 图例组件
          legend: {
            data:['销量']
          },
          // x轴
          xAxis: {
            data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
          },
          // y轴
          yAxis: {},
          // 图表设置,通过数组可配置多个
          series: [{
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }]
      };
      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
    </script>
  </body>
</head>
</html>

# 2、在vue中使用

安装插件:

npm i echarts --S

最新版为5.1.0,和4.x版本使用还是有所区别的。 4.x版本在vue中引入:

// 全局引入
// main.js
import echarts from 'echarts'
Vue.prototype.$echarts = echarts

// 按需引入
// main.js
import echarts from 'echarts'
// 组件中使用
import 'echarts/lib/chart/line'
// 如果import引入不行可以换成require
require('echarts/lib/chart/line')

5.x版本在vue中引入:

// 全局引入
// main.js
import * as echarts from 'echarts';
Vue.prototype.$echarts = echarts

// 在组件中按需引入
// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
import * as echarts from 'echarts/core';

// 引入柱状图图表,图表后缀都为 Chart
import {
  BarChart,
  PieChart,
  LineChart
} from 'echarts/charts';

// 引入提示框,标题,直角坐标系组件,组件后缀都为 Component
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  LegendComponent,
} from 'echarts/components';

// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
import {
  CanvasRenderer
} from 'echarts/renderers';

// 注册必须的组件
echarts.use(
  [TitleComponent, TooltipComponent, GridComponent, LegendComponent, BarChart, PieChart, LineChart, CanvasRenderer]
);

5.x版本的按需引入和4.x还是有很大区别的,不过图表的配置项还都是相同的。

# 配置参考

# 1、一个容器中配置多个图表

正常来说,一个容器上一个图表,然后在图表上可以渲染多条数据。但特殊情况下,一个容器内需要放多个图表。我们使用可以使用grid在容器中创建多个网格,每个网格可以渲染一个x轴和y轴,然后在xAxis/yAxis中使用gridIndex属性为每个坐标轴指定对应的网格,从0开始。然后再在series中每个图表中使用xAxisIndex/yAxisIndex属性为其指定在哪个坐标轴中显示,和gridIndex对应。这样就可以在一个容器中渲染多个图表或者内容了。

在坐标轴上使用 inverse: true可以让该轴进行反转,实现上下共用一个轴的情况。

option = {
  // 坐标轴指示器,在多个轴上显示,全部为'all'
  // axisPointer: {
  //   link: {xAxisIndex: [0, 1, ...]},
  // },

  // 渲染多个网格
  grid: [
    {left: '7%', top: '7%', width: '38%', height: '38%'},
    {right: '7%', top: '7%', width: '38%', height: '38%'},
    {left: '7%', bottom: '7%', width: '38%', height: '38%'},
    {right: '7%', bottom: '7%', width: '38%', height: '38%'}
  ],

  // 为每个坐标轴指定对应网格
  xAxis: [
    {gridIndex: 0, data: xData},
    {gridIndex: 1, data: xData},
    {gridIndex: 2, data: xData},
    {gridIndex: 3, data: xData}
  ],
  yAxis: [
    {gridIndex: 0, data: xData},
    {gridIndex: 1, data: xData},
    {gridIndex: 2, data: xData},
    {gridIndex: 3, data: xData}
  ],

  // 为每个图表指定x,y轴
  series: [
    {
      name: 'I',
      type: 'line',
      xAxisIndex: 0,
      yAxisIndex: 0,
      data: dataAll[0],
    },
    {
      name: 'II',
      type: 'line',
      xAxisIndex: 1,
      yAxisIndex: 1,
      data: dataAll[1],
    },
    {
      name: 'III',
      type: 'line',
      xAxisIndex: 2,
      yAxisIndex: 2,
      data: dataAll[2],
    },
    {
      name: 'IV',
      type: 'line',
      xAxisIndex: 3,
      yAxisIndex: 3,
      data: dataAll[3],
    }
  ]

# 2、嵌套饼状图

// 基于准备好的dom,初始化echarts实例
let myChart = echarts.init(document.getElementById('id'));
const legendData = [{name: '餐饮', value: 32},{name: '服务', value: 98},{name: '建筑', value: 24},{name: '软件', value: 64}];
// 绘制图表
let option = {
  title: {
    text: '学生分布情况',
    left: '24',
    top: '24',
    textStyle: {
      color: '#262F4D',
      fontWeight: 'Bold',
      fontSize: 18
    }
  },
  tooltip: {
    trigger: 'item',
    formatter: '{b}: {c} ({d}%)'
  },
  legend: {
    left: 30,
    top: 150,
    x: 'left',
    orient: 'vertical',
    icon: 'circle',
    itemHeight: 10,
    itemGap: 10,
    formatter: function (name) {
      let singleData = legendData.filter(item => item.name == name)
      return '{a|' + name + '} {b|' + singleData[0].value + '人}'
    },
    textStyle: {
      color: '#262F4D',
      fontSize: 16,
      rich: {
        a: {},
        b: {
          align: 'right',
          padding: [0, 0, 0, 20],
        }
      }
    },
    data: legendData
  },
  color: ['#FD9681', '#4D81E7', '#47D7EA', '#AF87FD', '#4E7AFF', '#A8BEFF', '#1AE1E5', '#2194FF', '#4ECB73'],
  series: [{
      top: 30,
      type: 'pie',
      selectedMode: 'single',
      selectedOffset: 5,
      radius: [0, '30%'],
      label: {
        formatter: '{b}({c}) \n {d|{d}%}',
        edgeDistance: 20,
        lineHeight: 20,
        color: '#262F4D',
        rich: {
          d: {
            lineHeight: 18,
            align: 'left'
          }
        },
      },
      select: {
        label: {
          color: '#262F4D'
        }
      },
      labelLine: {
        show: false
      },
      avoidLabelOverlap: false,
      data: [{name: '考研', value: 200},{name: '考公', value: 320},{name: '考编', value: 154},{name: '就业', value: 421}]
    },
    {
      top: 30,
      type: 'pie',
      radius: ['43%', '55%'],
      labelLine: {
        length: 20,
      },
      label: {
        formatter: '{b}({c}) \n {d|{d}%}',
        edgeDistance: 20,
        lineHeight: 20,
        color: '#262F4D',
        rich: {
          d: {
            lineHeight: 18,
            align: 'left'
          }
        },
      },
      labelLayout: function (params) {
        var isLeft = params.labelRect.x < myChart.getWidth() / 2;
        var points = params.labelLinePoints;
        // Update the end point.
        points[2][0] = isLeft ?
          params.labelRect.x :
          params.labelRect.x + params.labelRect.width;
        return {
          labelLinePoints: points
        };
      },
      data: [{name: '餐饮', value: 32},{name: '服务', value: 98},{name: '建筑', value: 24},{name: '软件', value: 64}]
    }
  ]
};

option && myChart.setOption(option);
window.addEventListener('resize', () => {
  myChart.resize();
})

# 3、折线面积图

smooth设置折线平滑显示,然后通过areaStyle设置折线包含的面积背景。

let myChart = echarts.init(document.getElementById('id'));
var option = {
  title: {
    text: '学生认证动态',
    left: '24',
    textStyle: {
      color: '#262F4D',
      fontWeight: 'Bold',
      fontSize: 18
    },
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: { // 坐标轴指示器,坐标轴触发有效
      type: 'line' // 默认为直线,可选为:'line' | 'shadow'
    }
  },
  grid: {
    x: 50,
    y: 80,
    x2: 50,
    y2: 30
  },
  xAxis: [{
    type: 'category',
    // x轴
    axisLine: {
      lineStyle: {
        color: '#DBDBDB'
      }
    },
    // 底部刻度线
    axisTick: {
      show: false
    },
    // 底部文本样式
    axisLabel: {
      color: '#333951',
      margin: 15
    },
    // 两边是否留白
    boundaryGap: false,
    data: xData
  }],
  yAxis: [{
    name: '每日认证趋势',
    // nameGap: -5,
    nameTextStyle: {
      color: '#999',
      fontSize: 14,
      align: 'left'
    },
    type: 'value',
    axisLine: {
      show: false
    },
    axisLabel: {
      color: '#999'
    },
    splitLine: {
      show: true,
      lineStyle: {
        type: 'dashed'
      }
    },
    // 背景
    // splitArea: {
    //   show: true
    // }
  }],
  series: [
    {
      type: 'line',
      // 关键
      smooth: true,
      // 纯背景
      areaStyle: {
        color: '#ccc',
        opacity: 0.7
      },
    },
    {
      type: 'line',
      smooth: true,
      // 渐变背景
      areaStyle: {
        opacity: 0.3,
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
          offset: 0,
          color: '#4E7AFF'
        }, {
          offset: 1,
          color: '#CCDDFF'
        }])
      },
      data: data
    }
  ]
}
option && myChart.setOption(option);
window.onresize = () => {
  myChart.resize();
}

# 4、上下对折折线图

// 基于准备好的dom,初始化echarts实例
this.myChart = this.$echarts.init(document.getElementById('id'));
this.option = {
  title: {
    text: '上下对折折线图',
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      type: 'line'
    },
    formatter: function(params) {
      let formatList = `${params[0].axisValue}<br/>`;
      params.forEach((item, index) => {
        const colorIndex = that.colorIndex ? that.colorIndex : index % (params.length / 2);
        if (index == params.length / 2) {
          formatList+= `<br/> ${item.marker} ${item.seriesName}: ${item.value}<br/>`
        } else {
          formatList+= `${item.marker} ${item.seriesName}: ${item.value}<br/>`
        }
      })
      return formatList
    }
  },
  axisPointer: {
    link: {xAxisIndex: 'all'},
  },
  grid: [
    {
      top: '5%',
      left: '80',
      right: '30',
      height: '40%',
    },
    {
      top: '45%',
      left: '80',
      right: '30',
      height: '40%'
    }
  ],
  xAxis: [
    {
      type: 'category',
      data: xData,
      boundaryGap : false,
      splitLine: {show: false},
      axisLabel: {show: false},
      axisTick: {show: false},
      axisPointer: {
        z: 100
      }
    },
    {
      type: 'category',
      gridIndex: 1,
      boundaryGap : false,
      axisLine: {
        lineStyle: {
          color: '#ccc'
        }
      },
      splitLine: {show: false},
      axisTick: {show: false},
      axisLabel: {
        show: true,
        color: '#444'
      },
      data: ['星期一','星期二','星期三','星期四','星期五','星期六','星期日'],
    }
  ],
  yAxis: [
    {
      name: '接收流量',
      nameLocation: 'middle',
      nameRotate: '-90',
      nameGap: 60,
      nameTextStyle: {
        color: '#999',
        fontSize: 12,
      },
      axisLine: {show: false},
      axisTick: {show: false},
    },
    {
      inverse: true,
      name: '发送流量',
      nameLocation: 'middle',
      nameRotate: '-90',
      nameGap: 60,
      nameTextStyle: {
        color: '#999',
        fontSize: 12,
      },
      gridIndex: 1,
      axisLine: {show: false},
      axisTick: {show: false},
    }
  ],
  // 数据缩放
  dataZoom: [{
    show: true,
    type: 'inside',
    start: 0,
    end: 20,
    xAxisIndex: [0, 1]
  }, {
    show: true,
    start: 0,
    end: 20,
    xAxisIndex: [0, 1]
  }],
  series: [
    {
      name: '上折线名称',
      type: 'line',
      showSymbol: false,
      smooth: true,
      lineStyle: {
        color: 'blue',
        opacity: 0.3
      },
      areaStyle: {
        color: 'blue',
        opacity: 0.7
      },
      data: [3,4,1,5,6,3,9],
    },
    {
      name: '下折线名称',
      type: 'line',
      showSymbol: false,
      smooth: true,
      lineStyle: {
        color: 'red',
        opacity: 0.3
      },
      areaStyle: {
        color: 'red',
        opacity: 0.7
      },
      xAxisIndex: 1,
      yAxisIndex: 1,
      data: [3,1,5,3,7,2,5],
    }
  ]
}
// 绘制图表
this.option && this.myChart.setOption(this.option);
window.onresize = () => {
  this.myChart.resize();
}

# 5、叠加柱状图

  let myChart = echarts.init(document.getElementById('id'));
  // 绘制图表
  let option = {
    title: {
      text: '验证分布',
      left: '24',
      textStyle: {
        color: '#262F4D',
        fontWeight: 'Bold',
        fontSize: 18
      }
    },
    tooltip: {
      trigger: 'axis',
      axisPointer: { // 坐标轴指示器,坐标轴触发有效
        type: 'none' // 默认为直线,可选为:'line' | 'shadow'
      }
    },
    grid: {
      x: 50,
      y: 80,
      x2: 50,
      y2: 30
    },
    legend: {
      right: 20,
      top: 5,
    },
    xAxis: [{
      type: 'category',
      // x轴
      axisLine: {
        lineStyle: {
          color: '#DBDBDB'
        }
      },
      // 底部刻度线
      axisTick: {
        show: false
      },
      // 底部文本样式
      axisLabel: {
        color: '#333951',
        interval: 0 //全部显示横坐标
      },
      data: ['星期一','星期二','星期三','星期四','星期五','星期六','星期日']
    }],
    yAxis: [{
      type: 'value',
      axisLine: {
        show: false
      },
      axisLabel: {
        color: '#999'
      },
      splitLine: {
        show: false
      },
      // 背景
      splitArea: {
        show: true
      }
    }],
    series: [{
        showBackground: false,
        name: '已验证',
        type: 'bar',
        stack: '百分比',
        itemStyle: {
          color: '#4D81E7'
        },
        barWidth: 20,
        data: [2,1,3,2,2,1,3]
      },
      {
        showBackground: false,
        name: '未验证',
        type: 'bar',
        stack: '百分比',
        itemStyle: {
          color: '#CCDDFF'
        },
        barWidth: 20,
        data: [7,12, 13, 9, 21, 28, 31]
      }
    ]
  };
  option && myChart.setOption(option);
  window.onresize = () => {
    myChart.resize();
  }

# 6、tooltip的formatter配置

tooltip设置鼠标经过时显示的提示框。正常都是自动显示series设置的name和当前的值,但有时需要设置特殊的值和样式时,就需要通过formatter函数来配置了。 formatter有两种形式来配置 字符串模板和回调函数。

字符串模板:

// 字符串模板搭配 textStyle.rich使用

formatter: [
    '{a|这段文本采用样式a}',
    '{b|这段文本采用样式b}这段用默认样式{x|这段用样式x}'
  ],

  rich: {
  a: {
    color: 'red',
    lineHeight: 10
  },
  b: {
    backgroundColor: {
        image: 'xxx/xxx.jpg'
    },
    height: 40
  },
}

回调函数:

// 回调函数通过对参数params拼接的字符串来显示
formatter: function(params) {
  console.log(params)

  let formatList = `${params[0].axisValue}<br/>`;
  params.forEach(item => {
    formatList+= `${item.marker} ${item.seriesName}: ${item.value}% <br/>`
  })
  return formatList
}

# 7、图表自适应

let myChart = this.$echarts.init(document.getElementById('id'));
window.onresize = () => {
  myChart.resize();
}

# 8、动态更新图表

通过echarts实例的setOption(option)来更新数据,可能有的版本直接更新不起效果,先要用实例的clear()方法清除实例,然后再重新渲染echarts。

this.myChart = this.$echarts.init(document.getElementById('id'));
this.myChart.clear();
this.getOption(); // 设置option,更新data数据,然后重新渲染
this.option && this.myChart.setOption(this.option);

# 9、柱状图颜色渐变

和折线面积图一样,使用 new echarts.graphic.LinearGradient() 来对 color 进行赋值就可以设置渐变色了。offset为 0~1 ,颜色从0到1逐渐渐变。

series: [
	{
		type: 'bar',
		showBackground: true,
		itemStyle: {
			color: new echarts.graphic.LinearGradient(
				0, 0, 0, 1,
				[
					{offset: 0, color: '#83bff6'},
					{offset: 0.5, color: '#188df0'},
					{offset: 1, color: '#188df0'}
				]
			)
		},
		data: data
	}
]

# 10、柱状图可点击

和折线面积图一样,使用 new echarts.graphic.LinearGradient() 来对 color 进行赋值就可以设置渐变色了。offset为 0~1 ,颜色从0到1逐渐渐变。

let myChart = echarts.init(document.getElementById('id'));
let option = {};
option && myChart.setOption(option);
window.onresize = () => {
  myChart.resize();
}

//点击柱状图
myChart.on('click', (params) =>{
	console.log(params);
});

# 事件行为

# 1、监听事件

echarts监听的事件可以对整个echart或者某个区域进行监听,大致分为两种,一种是鼠标事件,通过鼠标操作来触发,一种是图形上的事件,通过调用dispatchAction触发的。events事件 (opens new window)

对整个图表进行监听:

// 监听鼠标事件
myChart.on('click', function (params) {
    console.log(params);
});
myChart.on('mouseover', function (params) {
    console.log(params);
});

// 监听触发行为
myChart.on('highlight', function (params) {
    console.log(params);
});
myChart.on('selectchanged', function (params) {
    console.log(params);
});

监听某个区域或某类行为的事件:

通过传入第二个参数,来指定监听某个区域或者某类行为的事件。on监听事件 (opens new window)

// 指定的组件或者元素
chart.on('click', 'series', function () {...});
chart.on('click', 'series.line', function () {...});
chart.on('click', 'xAxis.category', function () {...});

// 传入Object指定
chart.on('mouseover', {
    dataIndex: number // 数据项 index
    name: string // 数据项 name
    dataType: string // 数据项 type,如关系图中的 'node', 'edge'
    element: string // 自定义系列中的 el 的 name
}, function () {});

解绑事件:

和绑定事件出入参数一样,如果第二个参数不传则解绑所有的该事件。

// 指定的组件或者元素
chart.off('click', 'series');
chart.off('click', 'series.line');
chart.off('click', 'xAxis.category');

// 传入Object指定
chart.off('mouseover', {
    dataIndex: number // 数据项 index
    name: string // 数据项 name
    dataType: string // 数据项 type,如关系图中的 'node', 'edge'
    element: string // 自定义系列中的 el 的 name
});

# 2、触发行为

通过dispatchAction触发图表上的行为,如高亮、选中、tooltip、legend等常见行为。

// 如果要高亮系列:
chart.dispatchAction({
    type: 'highlight',
    // 用 index 或 id 或 name 来指定系列。
    // 可以使用数组指定多个系列。
    seriesIndex?: number | number[],
    seriesId?: string | string[],
    seriesName?: string | string[],
    // 数据项的 index,如果不指定也可以通过 name 属性根据名称指定数据项
    dataIndex?: number | number[],
    // 可选,数据项名称,在有 dataIndex 的时候忽略
    name?: string | string[],
});

// 如果要取消高亮系列:
chart.dispatchAction({
    type: 'downplay',
    // 用 index 或 id 或 name 来指定系列。
    // 可以使用数组指定多个系列。
    seriesIndex?: number | number[],
    seriesId?: string | string[],
    seriesName?: string | string[],
    // 数据项的 index,如果不指定也可以通过 name 属性根据名称指定数据项
    dataIndex?: number | number[],
    // 可选,数据项名称,在有 dataIndex 的时候忽略
    name?: string | string[],
})

# 地图配置

# 1、通用地图配置

使用echart展示地图,需要自己获取相关geojson地图数据进行渲染地图,获取数据后进行注册就可以展示地图了。

获取数据:世界地图数据 (opens new window)数据可视化平台 (opens new window)

// 引入Echarts
import * as echarts from 'echarts/core'
import { BarChart, PieChart, LineChart, LinesChart, EffectScatterChart, ScatterChart, MapChart } from 'echarts/charts'
import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, VisualMapComponent, PolarComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
echarts.use([TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, VisualMapComponent, LinesChart, EffectScatterChart, ScatterChart, BarChart, PieChart, LineChart, MapChart, CanvasRenderer, PolarComponent])

地图数据映射图

import anhuiJson from '@/json/anhui.json'

drawMapEchart() {
  const mapData = [
    {
      name: '合肥市',
      value: 100
    },
    {
      name: '芜湖市',
      value: 55
    }
  ]
  const maxValue = Math.max(...mapData.map(el => el.value))
  echarts.registerMap('anhui', anhuiJson)
  const mapChart = echarts.init(document.getElementById('map-echart'))
  mapChart.setOption({
    tooltip: {
      trigger: 'item',
      formatter: '{b}<br/>{c} (个)'
    },
    visualMap: {
      min: 0,
      max: maxValue,
      text: ['高', '低'],
      realtime: false,
      calculable: true,
      inRange: {
        color: ['lightskyblue', 'yellow', 'orangered']
      }
    },
    series: [
      {
        type: 'map',
        map: 'anhui',
        roam: false,
        zoom: 1.2,
        // aspectScale: 0.9,
        label: {
          show: true,
          color: '#fff'
        },
        // itemStyle: {
        //   borderColor: '#2ab8ff',
        //   borderWidth: 1.5,
        //   areaColor: '#12235c'
        // },
        // emphasis: {
        //   itemStyle: {
        //     // borderColor: '#12235c',
        //     areaColor: '#2ab8ff'
        //   }
        // },
        data: mapData
      }
    ]
  })
  window.addEventListener('resize', () => {
    mapChart.resize()
  })
}

全球地图:

全球地图可以分为以中国为中心和通用视图两种,以中国为中心的数据,正常的坐标经度需要进行转换才行,如果经度小于30度需要加上360度。

全球飞线图-中国视角

drawMapEchart() {
  const geoCoordMap = {
    安徽: [117.37, 31.586],
    尼日利亚: [-4.388361, 11.186148],
    美国洛杉矶: [-118.24311, 34.052713],
    丹麦: [12.34, 55.41],
    美国芝加哥: [-87.801833, 41.870975],
    加纳库马西: [-4.62829, 7.72415]
  }
  for (const key in geoCoordMap) {
    const coordX = geoCoordMap[key][0]
    if (coordX <= -30) {
      geoCoordMap[key][0] = 360 + coordX
    }
  }
  const BJData = [
    [
      {
        name: '美国洛杉矶',
        value: 2370
      },
      {
        name: '安徽'
      }
    ],
    [
      {
        name: '丹麦',
        value: 3130
      },
      {
        name: '安徽'
      }
    ],
    [
      {
        name: '美国芝加哥',
        value: 2350
      },
      {
        name: '安徽'
      }
    ],
    [
      {
        name: '加纳库马西',
        value: 5120
      },
      {
        name: '安徽'
      }
    ],
    [
      {
        name: '英国曼彻斯特',
        value: 3110
      },
      {
        name: '安徽'
      }
    ],
    [
      {
        name: '德国汉堡',
        value: 6280
      },
      {
        name: '安徽'
      }
    ]
  ]
  const convertData = function (data) {
    const res = []
    for (let i = 0; i < data.length; i++) {
      const dataItem = data[i]
      const fromCoord = geoCoordMap[dataItem[0].name]
      const toCoord = geoCoordMap[dataItem[1].name]
      // 箭头方向
      if (fromCoord && toCoord) {
        res.push([
          {
            coord: toCoord
          },
          {
            coord: fromCoord,
            value: dataItem[0].value
          }
        ])
      }
    }
    return res
  }

  echarts.registerMap('world', mapJson)
  const serveMap = echarts.init(document.getElementById('map-chart2'))
  const series = []
  const origin = [['安徽', BJData]]
  origin.forEach((item, i) => {
    series.push({
      name: item[2],
      type: 'lines',
      zlevel: 2,
      effect: {
        show: true,
        period: 4, // 箭头指向速度,值越小速度越快
        trailLength: 0.02, // 特效尾迹长度[0,1]值越大,尾迹越长重
        symbol: 'arrow', // 箭头图标
        symbolSize: 5 // 图标大小
      },
      lineStyle: {
        normal: {
          color: '#01e2f6',
          width: 1, // 尾迹线条宽度
          opacity: 1, // 尾迹线条透明度
          curveness: 0.3 // 尾迹线条曲直度
        }
      },
      label: {
        normal: {
          show: false,
          position: 'middle',
          formatter: '{b}'
        }
      },
      data: convertData(item[1])
    })
    series.push({
      type: 'effectScatter',
      coordinateSystem: 'geo',
      zlevel: 2,
      rippleEffect: {
        // 涟漪特效
        period: 4, // 动画时间,值越小速度越快
        brushType: 'stroke', // 波纹绘制方式 stroke, fill
        scale: 4 // 波纹圆环最大限制,值越大波纹越大
      },
      label: {
        normal: {
          show: true,
          position: 'right', // 显示位置
          offset: [5, 0], // 偏移设置
          formatter: '{b}', // 圆环显示文字
          textStyle: {
            color: '#fff'
          }
        },
        emphasis: {
          show: true
        }
      },
      symbol: 'circle',
      symbolSize: function (val) {
        return 5
      },
      itemStyle: {
        normal: {
          show: true,
          color: '#01e2f6'
        }
      },
      data: item[1].map(function (dataItem) {
        return {
          name: dataItem[0].name,
          value: geoCoordMap[dataItem[0].name].concat([dataItem[0].value])
        }
      })
    })
    // 被攻击点
    series.push({
      type: 'scatter',
      coordinateSystem: 'geo',
      zlevel: 2,
      rippleEffect: {
        period: 4,
        brushType: 'stroke',
        scale: 4
      },
      label: {
        normal: {
          show: true,
          position: 'right',
          color: '#00ffff',
          formatter: '{b}',
          textStyle: {
            color: '#fff'
          }
        },
        emphasis: {
          show: true
        }
      },
      symbol: 'pin',
      symbolSize: 30,
      itemStyle: {
        normal: {
          show: true,
          color: '#01e2f6'
        }
      },
      data: [
        {
          name: item[0],
          value: geoCoordMap[item[0]].concat([10])
        }
      ]
    })
  })

  serveMap.setOption({
    // backgroundColor: '#e0e3e3',
    tooltip: {
      trigger: 'item',
      formatter: function (params) {
        return `${params.name}${params.value[2]}`
      }
    },
    geo: {
      map: 'world',
      roam: false, // 是否允许缩放
      zoom: 1.2,
      layoutCenter: ['50%', '50%'], // 地图位置
      ayoutSize: '180%',
      // 通过经纬度控制地图显示范围
      // boundingCoords: [
      //   // 定位左上角经纬度
      //   [60, 90],
      //   // 定位右下角经纬度
      //   [120, -20]
      // ],
      // layoutSize: '180%',
      label: {
        show: false
      },
      itemStyle: {
        areaColor: '#3e7de9',
        borderColor: '#55c1ef',
        emphasis: {
          areaColor: '#2a333d' // 鼠标悬浮背景色
        }
      },
      tooltip: {
        show: false
      }
    },
    series: series
  })
  window.addEventListener('resize', () => {
    serveMap.resize()
  })
}

# 2、3D地图

3D地图需要引入3D地图插件,配置项参考GL配置,上面的地图都需要使用3D的,不能再像平面那样使用了。3D地图 (opens new window)

安装:

npm i echarts-gl --save

使用

// 引入Echarts
import 'echarts-gl' // 3D地图插件
import * as echarts from 'echarts/core'
import { BarChart, PieChart, LineChart, LinesChart, EffectScatterChart, ScatterChart, MapChart } from 'echarts/charts'
import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, VisualMapComponent, PolarComponent, DataZoomComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
echarts.use([TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, VisualMapComponent, LinesChart, EffectScatterChart, ScatterChart, BarChart, PieChart, LineChart, MapChart, CanvasRenderer, PolarComponent, DataZoomComponent])

3D地图+3D柱状图展示数据

import anhuiMap from '@/json/anhui.json'

drawMapEchart() {
  if (this.mapChart) this.mapChart.dispose()
  const barData = [
    {
      name: '合肥市',
      value: 100
    },
    {
      name: '芜湖市',
      value: 55
    }
  ]
  const mapData = {
    合肥市: [117.37, 31.586],
    六安市: [116.27, 31.786],
    滁州市: [118.07, 32.486],
    蚌埠市: [117.27, 33.086],
    淮南市: [116.67, 32.786],
    宿州市: [117.77, 33.486],
    淮北市: [116.67, 33.686],
    亳州市: [116.27, 33.386],
    阜阳市: [115.57, 32.986],
    安庆市: [116.47, 30.486],
    池州市: [117.47, 30.386],
    黄山市: [118.17, 29.886],
    宣城市: [119.27, 30.916],
    芜湖市: [118.17, 31.186],
    马鞍山市: [118.47, 31.616],
    铜陵市: [117.87, 30.956]
  }

  const convertData = function (data) {
    const res = []
    for (let i = 0; i < data.length; i++) {
      const geoCoord = mapData[data[i].name]
      if (geoCoord) {
        res.push({
          name: data[i].name,
          value: geoCoord.concat(data[i].value)
        })
      }
    }
    return res
  }

  const maxValue = Math.max(...barData.map(el => el.value))

  echarts.registerMap('anhui', anhuiMap)
  this.mapChart = echarts.init(document.getElementById('map-chart1'))
  this.mapChart.setOption({
    backgroundColor: 'transparent',
    tooltip: {
      trigger: 'item',
      textStyle: {
        color: '#333'
      },
      formatter(item) {
        return `${item.name}  ${areaState[item.name].event_nums}`
      }
    },
    visualMap: [
      {
        min: 0,
        max: maxValue,
        text: ['高', '低'],
        textStyle: {
          color: '#fff'
        },
        realtime: false,
        calculable: true,
        inRange: {
          color: ['lightskyblue', 'yellow', 'orangered']
        }
      }
    ],
    geo3D: {
      map: 'anhui',
      roam: true,
      // boxDepth: 100, // 地图倾斜度
      regionHeight: 4, // 地图厚度
      label: {
        show: true, // 是否显示市
        color: '#fff', // 文字颜色
        fontSize: 10 // 文字大小
      },
      itemStyle: {
        color: '#244779',
        opacity: 1, // 透明度
        borderWidth: 1.5, // 分界线宽度
        borderColor: '#207fce' // 分界线颜色
      },
      groundplane: {
        show: false
      },
      realisticMaterial: {
        detailTexture: '#fff', // 纹理贴图
        textureTiling: 1, // 纹理平铺,1是拉伸,数字表示纹理平铺次数
        roughness: 0, // 材质粗糙度,0完全光滑,1完全粗糙
        metalness: 0, // 0材质是非金属 ,1金属
        roughnessAdjust: 0
      },
      viewControl: {
        center: [10, -20, 0], // 旋转中心
        alpha: 55, // x轴
        beta: -10, // y轴
        distance: 160, // 地图视角 控制初始大小
        rotateSensitivity: 1, // 旋转
        zoomSensitivity: 1 // 缩放
      }
    },
    series: [
      {
        name: 'bar3D',
        type: 'bar3D',
        coordinateSystem: 'geo3D',
        barSize: 3, // 柱子粗细
        shading: 'lambert',
        opacity: 1,
        bevelSize: 0.3,
        label: {
          show: true,
          position: 'top',
          formatter: params => `${barData[params.dataIndex].value} `,
          color: '#fff',
          fontSize: 12, // 文字大小
          padding: [0, 4],
          alignText: 'center',
          lineHeight: 16,
          backgroundColor: 'rgba(0,0,0,0.32)', // 透明度0清空文字背景
          borderWidth: 1, // 分界线宽度
          borderRadius: 2,
          borderColor: 'rgba(0,0,0,0.4)' // 分界线颜色
        },
        data: convertData(barData)
      }
    ]
  })
  window.addEventListener('resize', () => {
    this.mapChart.resize()
  })
}

3D飞线图

# 3、使用地图插件

echarts提供了地图配置,但是地图数据和样式是需要自己引入和配置的,我们可以借助地图插件使用高德地图或者百度地图渲染我们的地图,不用在手动导入地图数据了。缺点就是无法使用全球地图。

使用地图插件和使用地图一样,需要先在index.html中引入地图的JS API脚本,脚本上需要一个地图的key,这个key可以去地图开放平台去创建应用获取。

引入后需要使用 ScatterChart 和 EffectScatterChart 两个组件。并且还需要引入地图扩展渲染地图,百度地图自带,直接使用,高德地图需要安装插件才能引入。

import { ScatterChart, EffectScatterChart } from 'echarts/components';
echarts.use([ScatterChart, EffectScatterChart])

# 百度地图扩展

百度地图扩展有一个问题,无法进行设置center居中展示。可能是引入方式或版本不对,或者某个参数影响了。还有获取的地图实例和百度地图JS API 方法有差别。

百度地图扩展 (opens new window)

引入

// index.html
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=你申请的AK"></script>
// vue import
// 引入地图扩展
import 'echarts/extension/bmap/bmap'

地图渲染通过扩展字段 bmap 进行设置地图一些基础的属性和样式,mapStyle进行设置样式。地图数据需要两个列表,一个是省市对应的数据,一个是省市对应的经纬度坐标。拿到这两个数据后,可以进行进行分级渲染地图上的点和目标。 API

扩展提供的API和图表地理坐标系组件geo配置一样,在此基础上可以查看geo配置项进行设置。

// 加载 bmap 组件
bmap: {
  // 百度地图中心经纬度。默认为 [104.114129, 37.550339]。
  center: [120.13066322374, 30.240018034923],
  // 百度地图缩放级别。默认为 5。
  zoom: 14,
  // 是否开启拖拽缩放,可以只设置 'scale' 或者 'move'。默认关闭。
  roam: true,
  // 百度地图的旧版自定义样式,见 https://lbsyun.baidu.com/custom/index.htm
  mapStyle: {},
  // 百度地图 3.0 之后的新版自定义样式,见 https://lbsyun.baidu.com/index.php?title=open/custom
  mapStyleV2: {},
  // 百度地图的初始化配置,见 https://lbsyun.baidu.com/cms/jsapi/reference/jsapi_reference.html#a0b1
  mapOptions: {
    // 禁用百度地图自带的底图可点功能
    enableMapClick: false
  }
},
series: [
  {
    type: 'scatter',
    // 使用百度地图坐标系
    coordinateSystem: 'bmap',
    // 数据格式跟在 geo 坐标系上一样,每一项都是 [经度,纬度,数值大小,其它维度...]
    data: [[120, 30, 1]],
    // 编码数据项中第三个元素作为 value 维度
    encode: {
      value: 2
    }
  }
]

示例

const myChart = echarts.init(document.getElementById('chart-map'))
// 绘制图表
const option = {
  title: {
  text: '全国主要城市空气质量 - 百度地图',
  subtext: 'data from PM25.in',
  sublink: 'http://www.pm25.in',
  left: 'center'
},
tooltip: {
  trigger: 'item'
},
bmap: {
  center: [104.114129, 37.550339],
  zoom: 5,
  roam: true,
  mapStyle: {
    styleJson: [
      {
        featureType: 'water',
        elementType: 'all',
        stylers: {
          color: '#d1d1d1'
        }
      },
      {
        featureType: 'land',
        elementType: 'all',
        stylers: {
          color: '#f3f3f3'
        }
      },
      {
        featureType: 'railway',
        elementType: 'all',
        stylers: {
          visibility: 'off'
        }
      },
      {
        featureType: 'highway',
        elementType: 'all',
        stylers: {
          color: '#fdfdfd'
        }
      },
      {
        featureType: 'highway',
        elementType: 'labels',
        stylers: {
          visibility: 'off'
        }
      },
      {
        featureType: 'arterial',
        elementType: 'geometry',
        stylers: {
          color: '#fefefe'
        }
      },
      {
        featureType: 'arterial',
        elementType: 'geometry.fill',
        stylers: {
          color: '#fefefe'
        }
      },
      {
        featureType: 'poi',
        elementType: 'all',
        stylers: {
          visibility: 'off'
        }
      },
      {
        featureType: 'green',
        elementType: 'all',
        stylers: {
          visibility: 'off'
        }
      },
      {
        featureType: 'subway',
        elementType: 'all',
        stylers: {
          visibility: 'off'
        }
      },
      {
        featureType: 'manmade',
        elementType: 'all',
        stylers: {
          color: '#d1d1d1'
        }
      },
      {
        featureType: 'local',
        elementType: 'all',
        stylers: {
          color: '#d1d1d1'
        }
      },
      {
        featureType: 'arterial',
        elementType: 'labels',
        stylers: {
          visibility: 'off'
        }
      },
      {
        featureType: 'boundary',
        elementType: 'all',
        stylers: {
          color: '#fefefe'
        }
        },
        {
          featureType: 'building',
          elementType: 'all',
          stylers: {
            color: '#d1d1d1'
          }
        },
        {
          featureType: 'label',
          elementType: 'labels.text.fill',
          stylers: {
            color: '#999999'
          }
        }
      ]
    }
  },
  series: [
    {
      name: 'pm2.5',
      type: 'scatter',
      coordinateSystem: 'bmap',
      data: this.convertData(this.mapData),
      symbolSize: function (val) {
        return val[2] / 10;
      },
      encode: {
        value: 2
      },
      label: {
        formatter: '{b}',
        position: 'right',
        show: false
      },
      emphasis: {
        label: {
          show: true
        }
      }
    },
    {
      name: 'Top 5',
      type: 'effectScatter',
      coordinateSystem: 'bmap',
      data: this.convertData(this.mapData.sort(function (a, b) { return b.value - a.value; }).slice(0, 6)),
      symbolSize: function (val) {
        return val[2] / 10;
      },
      encode: {
        value: 2
      },
      showEffectOn: 'render',
      rippleEffect: {
        brushType: 'stroke'
      },
      label: {
        formatter: '{b}',
        position: 'right',
        show: true
      },
      itemStyle: {
        shadowBlur: 10,
        shadowColor: '#333'
      },
      emphasis: {
        scale: true
      },
      zlevel: 1
    }
  ]
}
option && myChart.setOption(option)
window.addEventListener('resize', () => {
  myChart.resize()
})
// 获取百度地图实例,使用百度地图自带的控件
var bmap = chart.getModel().getComponent('bmap').getBMap();
bmap.addControl(new BMap.MapTypeControl());

# 高德地图扩展

高德地图扩展 (opens new window) 注册

高德地图开发平台 (opens new window) 引入

npm install echarts-extension-amap --save

不要使用文档上的地址,直接使用高德地图的引入方式,文档上的是阉割版的。

// index.html
<script type="text/javascript">
  window._AMapSecurityConfig = {
    securityJsCode:'06cc147e04bf5f17ac533f88ecb11f0a'
  }
</script>
<script type="text/javascript" src="https://webapi.amap.com/maps?v=1.4.15&key=你的key"></script> 
// vue import
// 引入地图扩展
import 'echarts-extension-amap'

高德地图通过amap字段来设置地图属性。 API

高德地图和百度地图设置自定义样式mapStyle有区别,需要去开发平台设置好后发布,直接引入对应的字符串。

[自定义样式](https://lbs.amap.com/api/javascript-api/guide/map/map-style/)
option = {
  // load amap component
  amap: {
    // enable 3D mode
    // Note that it's suggested to enable 3D mode to improve echarts rendering.
    viewMode: '3D',
    // initial options of AMap
    // See https://lbs.amap.com/api/javascript-api/reference/map#MapOption for details
    // initial map center [lng, lat]
    center: [108.39, 39.9],
    // initial map zoom
    zoom: 4,
    // whether the map and echarts automatically handles browser window resize to update itself.
    resizeEnable: true,
    // customized map style, see https://lbs.amap.com/dev/mapstyle/index for details
    mapStyle: 'amap://styles/dark',
    // whether echarts layer should be rendered when the map is moving. Default is true.
    // if false, it will only be re-rendered after the map `moveend`.
    // It's better to set this option to false if data is large.
    renderOnMoving: true,
    // the zIndex of echarts layer for AMap, default value is 2000.
    // deprecated since v1.9.0, use `echartsLayerInteractive` instead.
    echartsLayerZIndex: 2019,
    // whether echarts layer is interactive. Default value is true
    // supported since v1.9.0
    echartsLayerInteractive: true,
    // whether to enable large mode. Default value is false
    // supported since v1.9.0
    largeMode: false
    // Note: Please DO NOT use the initial option `layers` to add Satellite/RoadNet/Other layers now.
    // There are some bugs about it, we can use `amap.add` instead.
    // Refer to the codes at the bottom.
    // More initial options...
  },
  series: [
    {
      type: 'scatter',
      // use `amap` as the coordinate system
      coordinateSystem: 'amap',
      // data items [[lng, lat, value], [lng, lat, value], ...]
      data: [[120, 30, 8], [120.1, 30.2, 20]],
      encode: {
        // encode the third element of data item as the `value` dimension
        value: 2
      }
    }
  ]
};
// 获取实例
var amap = chart.getModel().getComponent('amap').getAMap();

示例

const myChart = echarts.init(document.getElementById('chart-map'))
// 绘制图表
const option = {
  tooltip: {
    trigger: 'item'
  },
  amap: {
    viewMode: '3D',
    center: [104.114129, 37.550339],
    zoom: 5,
    lang: 'zh_cn',
    resizeEnable: true,
    // 自定义样式 https://lbs.amap.com/api/javascript-api/guide/map/map-style/
    mapStyle: 'amap://styles/grey'
  },
  series: [
    {
      name: 'pm2.5',
      type: 'scatter',
      coordinateSystem: 'amap',
      data: this.convertData(this.mapData),
      symbolSize: function (val) {
        return val[2] / 10
      },
      encode: {
        value: 2
      },
      label: {
        formatter: '{b}',
        position: 'right',
        show: false
      },
      emphasis: {
        label: {
          show: true
        }
      }
    },
    {
      name: 'Top 5',
      type: 'effectScatter',
      coordinateSystem: 'amap',
      data: this.convertData(
        this.mapData
          .sort(function (a, b) {
            return b.value - a.value
          })
          .slice(0, 6)
      ),
      symbolSize: function (val) {
        return val[2] / 10
      },
      encode: {
        value: 2
      },
      showEffectOn: 'render',
      rippleEffect: {
        brushType: 'stroke'
      },
      label: {
        formatter: '{b}',
        position: 'right',
        show: true
      },
      itemStyle: {
        shadowBlur: 10,
        shadowColor: '#333'
      },
      emphasis: {
        scale: true
      },
      zlevel: 1
    }
  ]
}
option && myChart.setOption(option)
window.addEventListener('resize', () => {
  myChart.resize()
})

// 获取地图实例,使用地图自带的控件
const amap = myChart.getModel().getComponent('amap').getAMap()
// amap.setMapStyle('amap://styles/whitesmoke')
amap.addControl(new AMap.Scale())
amap.addControl(new AMap.ToolBar())