前言
ECharts是我们常用的图表控件,功能特别强大,每次使用都要查API比较繁琐,这里就记录开发中常用的配置。
官网:https://echarts.apache.org/handbook/zh/get-started
配置项:https://echarts.apache.org/zh/option.html#title
第三方示例平台:https://www.makeapie.com/explore.html
引用
安装
代码语言:javascript复制npm install echarts --save
引用
代码语言:javascript复制import * as echarts from 'echarts';
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 绘制图表
myChart.setOption({
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
});
设置渲染方式
方式选择
在软硬件环境较好,数据量不大的场景下,两种渲染器都可以适用,并不需要太多纠结。
在环境较差,出现性能问题需要优化的场景下,可以通过试验来确定使用哪种渲染器。
比如有这些经验:
- 在须要创建很多 ECharts 实例且浏览器易崩溃的情况下(可能是因为 Canvas 数量多导致内存占用超出手机承受能力),可以使用 SVG 渲染器来进行改善。 大略得说,如果图表运行在低端安卓机,或者我们在使用一些特定图表如 水球图 等,SVG 渲染器可能效果更好。
- 数据量较大(经验判断 > 1k)、较多交互时,建议选择 Canvas 渲染器。
设置代码
代码语言:javascript复制// 使用 Canvas 渲染器(默认)
var chart = echarts.init(containerDom, null, { renderer: 'canvas' });
// 等价于:
var chart = echarts.init(containerDom);
// 使用 SVG 渲染器
var chart = echarts.init(containerDom, null, { renderer: 'svg' });
数据变化重新渲染
代码语言:javascript复制chart.setOption(option, notMerge, lazyUpdate);
或者:
代码语言:javascript复制chart.setOption(option, {
notMerge: false,
lazyUpdate: false,
silent: false
});
参数解释:
option
图表的配置项和数据,具体见配置项手册。notMerge
可选,是否不跟之前设置的 option 进行合并,默认为false
,即合并。lazyUpdate
可选,在设置完 option 后是否不立即更新图表,默认为false
,即立即更新。silent
可选,阻止调用 setOption 时抛出事件,默认为false
,即抛出事件。
第二个参数notMerge
,将它设置为true
即不合并之前的options,使用新的options。
响应容器的大小
代码语言:javascript复制var myChart = echarts.init(document.getElementById('main'));
window.onresize = function() {
myChart.resize();
};
注意和VUE结合使用时,一定要等待DOM渲染完毕再调用,否则不生效。
代码语言:javascript复制async leftbar_click() {
this.show_left = !this.show_left;
await this.$nextTick();
this.echart && this.echart.resize()
}
除了直接调用 resize()
不含参数的形式之外,还可以指定宽度和高度,实现图表大小不等于容器大小的效果。
myChart.resize({
width: 800,
height: 400
});
VUE封装图表自动缩放
VUE封装组件,让图表随页面变化自动缩放。
resize.js
代码语言:javascript复制import { debounce } from '@/utils/utils.js'
export default {
data() {
return {
$_mDom: null,
$_resizeHandler: null
}
},
mounted() {
this.initListener()
},
activated() {
if (!this.$_resizeHandler) {
// avoid duplication init
this.initListener()
}
// when keep-alive chart activated, auto resize
this.resize()
},
beforeDestroy() {
this.destroyListener()
},
deactivated() {
this.destroyListener()
},
methods: {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.$_resizeHandler()
}
},
initListener() {
this.$_resizeHandler = debounce(() => {
this.resize()
}, 100)
window.addEventListener('resize', this.$_resizeHandler)
this.$_mDom = document.getElementById('app')
this.$_mDom && this.$_mDom.addEventListener('transitionend', this.$_sidebarResizeHandler)
},
destroyListener() {
window.removeEventListener('resize', this.$_resizeHandler)
this.$_resizeHandler = null
this.$_mDom && this.$_mDom.removeEventListener('transitionend', this.$_sidebarResizeHandler)
},
resize() {
const { chart } = this
chart && chart.resize()
}
}
}
utils.js
代码语言:javascript复制/**
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function() {
// 据上一次触发时间间隔
const last = new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function(...args) {
context = this
timestamp = new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
自定义图表组件中调用
代码语言:javascript复制import * as echarts from 'echarts'
import resize from "@/components/charts/mixins/resize.js"
export default {
mixins: [resize],
data() {
return {
chart: null,
};
},
}
完整示例
PieRateChart.vue
代码语言:javascript复制<template>
<div :class="className" :style="{ height: height, width: width }"/>
</template>
<script>
import * as echarts from 'echarts'
import resize from "@/components/charts/mixins/resize.js"
export default {
mixins: [resize],
props: {
className: {
type: String,
default: "chart",
},
width: {
type: String,
default: 0,
},
height: {
type: String,
default: "300px",
},
title: {
type: String,
default: ''
},
mRate: {
type: String,
default: () => {
return "0"
}
}
},
data() {
return {
chart: null,
};
},
watch: {
mRate: function () {
this.initChart()
}
},
async mounted() {
await this.$nextTick();
this.initChart();
},
beforeDestroy() {
if (!this.chart) {
return;
}
this.chart.dispose();
this.chart = null;
},
methods: {
initChart() {
if (!this.chart) {
this.chart = echarts.init(this.$el);
}
let title = this.title;
let num = parseFloat(this.mRate);
let mydata = [num, 100 - num];
let opts = {
backgroundColor: '#ffffff',
tooltip: {
show: true,
},
color: ['#3c90f7', '#55bfc0'],
series: [
{
type: 'pie',
radius: ['60%', '80%'],
center: ['50%', '50%'],
hoverAnimation: true,
data: mydata,
itemStyle: {
normal: {
borderWidth: 2,
borderColor: '#ffffff',
},
},
label: {
show: false,
},
},
],
title: {
text: num '%',
top: '38%',
textAlign: 'center',
left: '49%',
textStyle: {
color: '#666',
fontSize: 16,
fontWeight: '400',
},
},
};
this.chart.setOption(opts)
},
}
};
</script>
配置项
title
标题组件,包含主标题和副标题。
代码语言:javascript复制title:{
show:true,
text : '',
textStyle:{
color: '#333' ,
fontStyle: 'normal' ,
fontWeight: 'bolder' ,
fontFamily: 'sans-serif' ,
fontSize: 18 ,
lineHeight ... ,
width ... ,
height ... ,
textBorderColor ... ,
textBorderWidth ... ,
textBorderType: 'solid' ,
textBorderDashOffset: 0 ,
textShadowColor: 'transparent' ,
textShadowBlur: 0 ,
textShadowOffsetX: 0 ,
textShadowOffsetY: 0 ,
overflow: 'none' ,
ellipsis: '...' ,
},
subtext: '' ,
subtextStyle:{},
zlevel: 0 ,
z: 2 ,
left: 'auto' ,
top: 'auto' ,
right: 'auto' ,
bottom: 'auto' ,
}
xAxis/yAxis
直角坐标系 grid 中的 x/y 轴,一般情况下单个 grid 组件最多只能放上下两个 x/y 轴,多于两个 x/y 轴需要通过配置 offset 属性防止同个位置多个x/y 轴的重叠。
代码语言:javascript复制xAxis:[
{
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
axisTick: {
alignWithLabel: true,
},
axisLabel: {
rotate: 20
},
nameLocation: "end",
name: "%"
},
]
axisTick:坐标轴刻度相关设置。
axisLabel:坐标轴刻度标签的相关设置。
name:相关的是轴线一侧的文字,如显示单位。
自定义标签内容
代码语言:javascript复制option = {
yAxis: {
axisLabel: {
formatter: '{value} 元',
align: 'center'
}
}
};
grid
直角坐标系内绘图网格,单个 grid 内最多可以放置上下两个 X 轴,左右两个 Y 轴。可以在网格上绘制折线图,柱状图,散点图(气泡图)。
代码语言:javascript复制grid: {
top: 20,
left: "2%",
right: "2%",
bottom: 10,
containLabel: true,
},
四个方向都可以用百分比和具体数值
containLabel
为true
时,left
right
top
bottom
width
height
决定的是包括了坐标轴标签在内的所有内容所形成的矩形的位置。
legend
图例组件。
图例组件展现了不同系列的标记(symbol),颜色和名字。可以通过点击图例控制哪些系列不显示。
代码语言:javascript复制legend: {
type: 'plain',
orient: 'horizontal',
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
bottom: 20,
align: 'left',
textStyle: {
color: '#333',
},
itemGap: 10,
formatter: (name) => {
let item = null;
for (const _item of chartData) {
if (_item.chainName === name) {
item = _item;
break;
}
}
if (item) {
return name ' ' item.total '次 ' ((item.successCount * 100) / item.total).toFixed(1) '%';
} else {
return name;
}
},
},
其中:
- itemGap:图例每项之间的间隔。横向布局时为水平间隔,纵向布局时为纵向间隔。
tooltip
悬浮框组件。
代码语言:javascript复制tooltip: {
show: true,
formatter: '{b0}成功率: {c0}%',
},
dataZoom
dataZoom
组件 用于区域缩放,从而能自由关注细节的数据信息,或者概览数据整体,或者去除离群点的影响。
dataZoom: {
right: '2%', //下滑块距离x轴底部的距离
top: '5%',
height: '90%', //下滑块手柄的高度调节
width: 20,
type: 'slider', //类型,滑动块插件
show: true, //是否显示下滑块
yAxisIndex: [0], //选择的y轴
start: 0, //初始数据显示多少
end: 100, //初始数据最多显示多少
},
series
折线图
代码语言:javascript复制series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line',
smooth: true
}
]
柱状图
代码语言:javascript复制series: [
{
type: 'bar',
showBackground: false,
data: valueData,
barWidth: 16,
itemStyle: {
normal: {
color: '#3c90f7',
barBorderRadius: [0, 8, 8, 0],
},
},
emphasis: {
itemStyle: {
color: specialColor,
},
},
},
],
其中:
- emphasis:高亮的图形样式和标签样式。
饼图
代码语言:javascript复制series: [
{
type: 'pie',
radius: ['30%', '45%'],
center: ['50%', '30%'],
hoverAnimation: true,
itemStyle: {
normal: {
borderWidth: 2,
borderColor: '#ffffff',
label: {
show: true,
position: 'outside',
color: '#ddd',
formatter: function (params) {
var percent = 0;
var total = 0;
for (var i = 0; i < mydata.length; i ) {
total = mydata[i].value;
}
percent = ((params.value / total) * 100).toFixed(0);
if (params.name !== '') {
return '名称:' params.name 'n' 'n' '比例:' percent '%';
} else {
return '';
}
},
},
labelLine: {
length: 30,
length2: 100,
show: true,
color: '#00ffff',
},
},
},
label: {
show: false,
},
data: mydata,
labelLine: {
show: true,
},
},
],
其它
多个图形
代码语言:javascript复制option = {
xAxis: [
{ type: 'category', gridIndex: 0, data: ['1月', '2月', '3月'] },
{ type: 'category', gridIndex: 1, data: ['4月', '5月', '6月'], position: 'top' },
],
yAxis: [
{ gridIndex: 0 },
{ gridIndex: 1, inverse: true }
],
grid: [
{ bottom: '55%' },
{ top: '55%' }
],
series: [
// 这几个系列会出现在第一个直角坐标系中
{ type: 'bar', data: [1, 5, 10] },
{ type: 'bar', data: [2, 4, 3] },
// 这几个系列会出现在第二个直角坐标系中,
{ type: 'line', xAxisIndex: 1, yAxisIndex: 1, data: [2, 5, 8] },
{ type: 'line', xAxisIndex: 1, yAxisIndex: 1, data: [8, 2, 6] },
],
};
关键的两个属性:
xAxis
中的position: 'top'
yAxis
中的inverse: true
效果如下:
渐变色
代码语言:javascript复制var specialColor = {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 0,
color: '#3c90f7', // 0% 处的颜色
},
{
offset: 1,
color: '#55bfc0', // 100% 处的颜色
},
],
globalCoord: false, // 缺省为 false
};
option={
series: [
{
type: 'bar',
showBackground: false,
data: [1,3,5,2,4],
barWidth: 16,
itemStyle: {
normal: {
color: '#3c90f7',
barBorderRadius: [0, 8, 8, 0],
},
},
emphasis: {
itemStyle: {
color: specialColor,
},
},
},
],
}